Python回顾与整理9:函数和函数式编程

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介:

  无论在什么编程语言中,函数都不可或缺,充分利用函数的特性,可以大大减少我们程序中的代码量。




1.什么是函数


        所谓函数,英文名为function,其实就是表示为实现一定功能的一段代码,显然,如果需要多次实现某一功能时,使用函数就是把重复代码放入其中,既节省空间,又有助于保持一致性(主要是修改代码时)。


(1)函数vs过程

        两者都是可以被调用的实体,过程是简单、没有返回值、特殊的函数。在Python中,过程就是函数,因为解释器会隐匿地返回默认值None。


(2)返回值与函数类型

        在C语言中,如果定义的一个函数没有返回值,则默认返回`void`,并且同时还要在定义函数时声明函数的类型(即返回值为void的函数类型)。

        在Python中,并不需要定义函数的返回值类型,函数可以返回不同类型的值,而如果没有返回值,则默认返回None:

1
2
3
4
5
6
7
8
9
10
>>>  def  hello():
...      print  'hello world!'
... 
>>> res  =  hello()
hello world!
>>> res
>>>  print  res
None
>>>  type (res)
< type  'NoneType' >

        另外需要注意的是,跟C语言一样,Python也只能返回一个值或对象,但也许你会看到下面这样的情况:

1
2
3
4
>>>  def  foo():
...      return  'xpleaf' 'clyyh'
... 
>>> res  =  foo()

        执行没有报错,似乎真的可以返回多个对象!但其实,它真的返回了一个对象:

1
2
3
4
>>> res
( 'xpleaf' 'clyyh' )
>>>  type (res)
< type  'tuple' >

        即在上面的函数中,其实是隐式地返回了一个元组,只是看起来像是返回了多个对象而已。显然,Python的这种特性要比C语言的灵活很多。关于返回值数量,可总结如下:

返回的对象的数目 Python实际返回的对象
0 None
1 object
>1 tuple




2.调用函数


(1)函数操作符

        其实就是使用圆括号来调用一个函数。


(2)关键字参数

        指的是在调用函数时,可以通过给指定参数传值,而不需要按照原来函数定义参数的顺序来传值。

        定义如下一个函数:

1
2
def  net_conn(host, port):
     net_conn_suite

        可以有如下调用方式:

  • 标准调用(非关键字参数调用)

1
2
# 给参数传递值时,必须按照原来参数定义的顺序传递值
net_conn( 'www.xpleaf.com' 80 )
  • 关键字参数调用

1
2
3
4
5
# 按照顺序给参数传递值
net_conn(host = 'www.xpleaf.com' , port = 80  )
 
# 不按照顺序给参数传递值
net_conn(port = 80 , host = 'www.xpleaf.com' )


(3)默认参数

        默认参数就是声明了默认值的参数,因为给参数赋予了默认值,所以在调用函数时,不向该参数传入值也是允许的,后面会有讨论。


(4)参数组

        通过一个把元组(非关键字参数)或字典(关键字参数)作为参数部分传递给函数,可以在Python中执行一个没有显式定义参数的函数。如下:

1
func( * tuple_grp_nonkw_args,  * * dict_trp_kw_args)

        当然也可以给出其它形参,包括标准的位置参数(既不是默认参数也不是关键字参数,即按照函数定义时参数的位置来给相应的参数传递值)和关键字参数(函数调用时指定给哪一个参数传递值,其实就是所谓关键字参数了),函数调用的完整语法如下:

1
func(positional_args, keyword_args,  * tuple_grp_nonkw_args,  * * dict_grp_kw_args)




3.创建函数


(1)def语句

        Python中使用def语句来定义函数,语法如下:

1
2
3
def  function_name(arguments):
     "function_documentation_string"
     function_body_suite

        即Python函数由标题行文档字符串函数体组成。


(2)声明与定义比较

        在C语言中,往往习惯于先在文件前声明一个函数,然后在文件末尾处才定义这个函数。而在Python中,声明与定义是被视为一体的,这是因为Python的函数由声明的标题行和随后定义的函数体组成。


(3)前向引用

        和大多数编程语言一样,如果在声明函数(在Python中其实也就是定义函数)前对其进行使用,就会有问题:

1
2
3
4
5
6
7
8
9
10
>>>  def  foo():
...      print  'in foo()'
...     bar()
... 
>>> foo()
in  foo()
Traceback (most recent call last):
   File  "<stdin>" , line  1 in  <module>
   File  "<stdin>" , line  3 in  foo
NameError:  global  name  'bar'  is  not  defined

        显然bar()还没有被声明(定义),在定义之后就可以使用了:

1
2
3
4
5
6
7
8
9
10
>>>  def  bar():
...      print  'in bar()'
... 
>>>  def  foo():
...      print  'in foo()'
...     bar()
...
>>> foo()
in  foo()
in  bar()

        当然,如果在一个脚本文件中,不按顺序给出定义bar()、定义foo()、执行foo()的顺序也是可以的:

1
2
3
4
5
6
7
8
9
def  foo():
     print  'in foo()'
     bar()
 
def  bar():
     print  'in bar()'
 
foo()
# 即相当于先声明foo(),再声明bar(),接着调用foo(),这里bar()已经存在了

        执行如下:

1
2
3
/usr/bin/python2 .7  /home/xpleaf/PycharmProjects/Python_book/11/test .py
in  foo()
in  bar()

        但如果在声明(定义)bar()前执行foo(),就会报错:

1
2
3
4
5
6
7
8
def  foo():
     print  'in foo()'
     bar()
 
foo()
 
def  bar():
     print  'in bar()'

        执行如下:

1
2
3
4
5
6
7
8
/usr/bin/python2 .7  /home/xpleaf/PycharmProjects/Python_book/11/test .py
in  foo()
Traceback (most recent call last):
   File  "/home/xpleaf/PycharmProjects/Python_book/11/test.py" , line 7,  in  <module>
     foo()
   File  "/home/xpleaf/PycharmProjects/Python_book/11/test.py" , line 4,  in  foo
     bar()
NameError: global name  'bar'  is not defined

        这是因为Python是一种动态语言。


(4)函数属性

        谈到函数属性,显然会涉及到名称空间的讨论,但这里先不对名称空间进行说明。

        函数属性是Python另外一个使用了句点属性标识并拥有名称空间的领域,这意味着,它是一个独立的名称空间,与函数内部所定义的变量(局部变量)所产生的名称空间没有关系,这点尤其要注意。

        看如下一个例子:

1
2
3
4
5
6
7
>>>  def  foo():
...      'foo() -- properly created doc string'
... 
>>>  dir (foo)
[ '__call__' '__class__' '__closure__' '__code__' '__defaults__' '__delattr__' '__dict__' '__doc__' '__format__' '__get__' '__getattribute__' '__globals__' '__hash__' '__init__' '__module__' '__name__' '__new__' '__reduce__' '__reduce_ex__' '__repr__' '__setattr__' '__sizeof__' '__str__' '__subclasshook__' 'func_closure' 'func_code' 'func_defaults' 'func_dict' 'func_doc' 'func_globals' 'func_name' ]
>>> foo.__doc__
'foo() -- properly created doc string'

        可以给foo()添加其它的属性:

1
2
3
4
5
6
7
8
9
>>> foo.version  =  0.1
>>> foo.writer  =  'xpleaf'
>>>  dir (foo)
[ '__call__' '__class__' '__closure__' '__code__' '__defaults__' '__delattr__' '__dict__' '__doc__' '__format__' '__get__' '__getattribute__' '__globals__' '__hash__' '__init__' '__module__' '__name__' '__new__' '__reduce__' '__reduce_ex__' '__repr__' '__setattr__' '__sizeof__' '__str__' '__subclasshook__' 'func_closure' 'func_code' 'func_defaults' 'func_dict' 'func_doc' 'func_globals' 'func_name' 'version' 'writer' ]
>>> foo.version
0.1
>>> foo.writer
'xpleaf'
# 可以看到version多了version和writer属性,并可以直接通过句点属性的方式进行调用

        当然,上面定义的这些函数属性也会保存在函数的__dict__属性字典中,并且可以通过字典的方式进行调用:

1
2
3
4
5
6
>>> foo.__dict__
{ 'writer' 'xpleaf' 'version' 0.1 }
>>> foo.__dict__[ 'writer' ]
'xpleaf'
>>> foo.__dict__[ 'version' ]
0.1

        但是对于函数本身已经存在的__attribute__类型的属性,不会保存在这个字典中,如__doc__属性。

        可以看下面的一个例子来理解“函数属性与函数局部变量分别拥有独立的名称空间”的特性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>>  def  info():
...     name  =  'xpleaf'
...     age  =  21
...      print  locals ()
... 
>>> info()
{ 'age' 21 'name' 'xpleaf' }
>>>
>>> info.name  =  'cl'
>>> info.age  =  20
>>> info.__dict__
{ 'age' 20 'name' 'cl' }
>>>
>>> info()
{ 'age' 21 'name' 'xpleaf' }


(5)内部/内嵌函数

        在函数中可以定义另外一个函数,举例如下:

1
2
3
4
5
6
7
8
9
>>>  def  foo():
...      def  bar():
...              print  'bar() called'
...      print  'foo() called'
...     bar()
... 
>>> foo()
foo() called
bar() called

        如果内部函数访问外部函数里定义的对象,那么就又涉及到闭包的问题,后面会有介绍。


(6)函数(与方法)装饰器

        在没有装饰器之前,如果要在类中定义一个静态方法,需要使用下面的方法:

1
2
3
class  MyClass( object ):
     def  staticFoo():
         staticFoo  =  staticmethod (staticFoo)

        即要在该静态方法中加入类似staticmethod()内建函数将该方法转换为静态方法,这显然非常麻烦,而有了装饰器之后,就可以写成下面这样:

1
2
3
4
class  MyClass( object ):
     @ staticmethod
     def  staticFoo():
         pass

        这样就简洁很多了。

(a)无参数装饰器    

  • 一个装饰器

        下面的情况:

1
2
3
@f
def  foo():
     pass

        其实就相当于:

1
2
3
def  foo():
     pass
foo  =  g(foo)
  • 多个装饰器

        下面的情况:

1
2
3
4
@g
@f
def  foo():
     pass

        就相当于:

1
2
3
def  foo():
     pass
foo  =  g(f(foo))


(b)含参数装饰器

  • 带有参数的一个装饰器

        下面的情况:

1
2
3
@decomaker (deco_args)
def  foo():
     pass

        就相当于:

1
2
3
def  foo():
     pass
foo  =  decomaker(deco_args)(foo)

        用这样的思想去理解就非常好理解了:decomaker()用deco_args做了些事并返回函数对象,而该函数对象正是以foo作为其参数的装饰器

        下面多个装饰器的例子也是按这样的思想去理解。

  • 带有参数的多个装饰器

        下面的情况:

1
2
3
4
@deco1 (deco_arg)
@deco2 ()
def  foo():
     pass

        就相当于:

1
2
3
def  foo():
     pass
foo  =  deco1(deco_arg)(deco2(foo))


(c)实践例子

        OK,有了上面的理论基础,理解下面一个较为复杂的装饰器就很容易了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from  functools  import  wraps
 
def  log(text):
     def  decorator(func):
         @wraps(func)                     #it works like:wraper.__name__ = func.__name__
         def  wrapper( * args,  * * kwargs):
             print  '%s %s():'  %  (text, func.__name__)
             return  func( * args,  * * kwargs)
         return  wrapper
     return  decorator
 
 
@log ( 'Hello' )
def  now(area):
     print  area,  '2016-01-23'
     
 
now( 'Beijing' )
print  'The name of function now() is:' , now.__name__

        执行如下:

1
2
3
4
/ usr / bin / python2. 7  / home / xpleaf / PycharmProjects / decorator_test / dec10.py
Hello now():
Beijing  2016 - 01 - 23
The name of function now()  is : now

对于该程序的执行过程,可以分析如下:

1.先执行log('Hello')函数,此时返回了一个新的函数,只不过其中的text变量被替换为'Hello',所以用来装饰now函数的新的装饰器如下:

1
2
3
4
5
6
def  decorator(func):
     @wraps(func)                     #it works like:wraper.__name__ = func.__name__
     def  wrapper( * args,  * * kwargs):
         print  '%s %s():'  %  ( 'Hello' , func.__name__)
         return  func( * args,  * * kwargs)
     return  wrapper

2.所以此时的now函数,就相当于:

1
now  =  decorator(now)

3.即now就相当于:

1
2
3
4
def  now( * args,  * * kwargs):
     print  '%s %s():'  %  ( 'Hello' , old_now.__name__)
     return  old_now( * args,  * * kwargs)
# 现在的函数名称变为了now而不是wrapper,是因为使用了wraps装饰器

   所以,输出的结果也就非常好理解了。

        关于wraps,它也是一个装饰器,使用它的作用是,被我们用自定义装饰器修改后的函数,它的函数名称,即func.__name__跟原来是一样的,而它的工作原理正如上面所提及的,即:

1
wraper.__name__  =  func.__name__

        也就是说,使用wraps可以不改变原来函数的属性,当然,上面只是简单说明了一下其工作原理,详细的可以参考wraps的源代码。

        在GitHub上给出了10个理解装饰器的例子,可以参考一下:https://github.com/xpleaf/decorator




4.传递参数


  • 对象特性

        在Python中,函数也是以对象的形式存在,因此可以通过引用传递的方式赋值给其它变量:

1
2
3
4
5
6
>>>  def  foo():
...      print  'in foo()'
... 
>>> bar  =  foo
>>> bar()
in  foo()

        需要注意的是,`foo`为函数对象的引用,而`foo()`则为函数对象的调用。但是因为函数对象最初是赋给foo的,所以函数对象的名字仍然是'foo',如下:

1
2
3
4
>>> foo.__name__
'foo'
>>> bar.__name__
'foo'
  • 参数传递

        因为函数本身也是一个对象,当然也就可以作为参数传递给其它函数,如下:

1
2
3
4
5
6
7
8
>>>  def  foo():
...      print  'in foo'
... 
>>>  def  bar(argfunc):
...     argfunc()
... 
>>> bar(foo)
in  foo

        其实就相当于把foo通过引用传递的方式赋值给argfunc,然后再调用,这跟对象特性中的原理是一样的。

        

        可以看下面的一个应用例子:

1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/env python
 
def  convert(func, seq):
     'conv, sequence of numbers to same type'
     return  [func(eachNum)  for  eachNum  in  seq]
 
myseq  =  ( 123 45.67 - 6.2e8 9999999999L )
 
print  convert( int , myseq)
print  convert( long , myseq)
print  convert( float , myseq)

        执行如下:

1
2
3
4
/ usr / bin / python2. 7  / home / xpleaf / PycharmProjects / Python_book / 11 / test.py
[ 123 45 - 620000000 9999999999 ]
[ 123L 45L - 620000000L 9999999999L ]
[ 123.0 45.67 - 620000000.0 9999999999.0 ]




5.Formal Arguments


        所谓Formal Arguments,即正式参数,包括位置参数和默认参数;而非正式参数,主要包括非关键字可变长参数(元组)和关键字可变长参数(字典)。

        在Python中,函数的形参集合由在调用函数时的所有参数组成,显然,传入的参数应该和函数原来定义的参数列表(形参)要精确的匹配。

        传入的参数主要包括如下:

  • 所有必要参数:即应该以正确的位置顺序来传入函数的参数

  • 关键字参数:按顺序或不按顺序,但这些关键字都应该是原来函数中有定义的

  • 默认参数:在函数定义时默认就赋了值的参数,调用函数时可以不指定默认参数

        函数执行时,就会为各个参数创建一个局部名称空间。


(1)位置参数

        所谓位置参数,即在调用函数时,应该以在被调用函数中定义的准确顺序来传递。

        当然,如果指定了该参数的关键字,就变成关键字参数了,调用函数时也就按照关键字参数的方式去给形参赋值了。


(2)默认参数

        默认参数就是在函数调用时,如果没有为参数提供值,则使用预先定义的默认值。不过需要注意的是,在Python中,在定义函数时,默认值应该在位置参数之后,即:

1
def  func(posargs, defarg1 = dval1, defarg2 = dval2,...)

        否则就会报错:

1
2
3
4
5
>>>  def  foo(host = 'www.xpleaf.com' , port):
...      print  "host:%s, port:%s"  %  (host, port)
... 
   File  "<stdin>" , line  1
SyntaxError: non - default argument follows default argument




6.可变长度的参数


        如果不确定函数中的参数数目,可以使用可变长度的参数,在Python中,主要包括两种可变长度参数类型,一种是不指定关键字的非关键字可变长参数(元组),而另外一种是指定关键字的关键字可变长参数(字典),不过需要注意如下规则:

  • 两种可变长度的参数都必须位于Formal Arguments(位置参数和默认参数)之后

  • 非关键字可变长参数需要在关键字可变长参数之前


(1)非关键字可变长参数(元组)

  • 语法

1
2
def  function_name([formal_args, ]  * vargs_tuple)
     function_body_suite

        举例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
>>>  def  tupleVarArgs(arg1, arg2 = 'defaultB' * theRest):
...      print  'formal arg 1:' , arg1
...      print  'formal arg 2:' , arg2
...      for  eachXtrArg  in  theRest:
...              print  'another arg:' , eachXtrArg
... 
>>> tupleVarArgs( 'abc' )
formal arg  1 : abc
formal arg  2 : defaultB
>>>
>>> tupleVarArgs( 23 4.56 )
formal arg  1 23
formal arg  2 4.56
>>> 
>>> tupleVarArgs( 'abc' 123 'xyz' 456.789 )
formal arg  1 : abc
formal arg  2 123
another arg: xyz
another arg:  456.789


(2)关键字可变长参数(字典)

        在关键字可变长参数中,参数被放入一个字典中,其中字典的键为参数名,值为相应的参数值。

  • 语法

1
2
def  function_name([formal_args, ][ * vargst, ]  * * vargsd):
     function_body_suite

        举例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
>>>  def  dictVarArgs(arg1, arg2 = 'defaultB' * * theRest):
...      print  'formal arg1:' , arg1
...      print  'formal arg2:' , arg2
...      for  eachXtrArg  in  theRest.keys():     # 使用dict.keys()只是为了说明theRest是一个字典
...              print  'Xtra arg %s: %s'  %  (eachXtrArg,  str (theRest[eachXtrArg])) 
... 
>>> dictVarArgs( 1220 740.0 , c = 'grail' )
formal arg1:  1220
formal arg2:  740.0
Xtra arg c: grail
>>> 
>>> dictVarArgs(arg2 = 'tales' , c = 123 , d = 'poe' , arg1 = 'mystery' )
formal arg1: mystery
formal arg2: tales
Xtra arg c:  123
Xtra arg d: poe
>>> 
>>> dictVarArgs( 'one' , d = 10 , e = 'zoo' , men = ( 'freud' 'gaudi' ))
formal arg1: one
formal arg2: defaultB
Xtra arg men: ( 'freud' 'gaudi' )
Xtra arg e: zoo
Xtra arg d:  10

        当然,非关键字可变长参数和关键字可变长参数也是可以同时使用的,只需要遵循参数的先后顺序规则即可,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>>  def  newfoo(arg1, arg2,  * nkw,  * * kw):
...      print  'arg1 is:' , arg1
...      print  'arg2 is:' , arg2
...      for  eachNKW  in  nkw:
...              print  'additional non-keyword arg:' , eachNKW
...      for  eachKW  in  kw.keys():
...              print  "additional keyword arg '%s': %s"  %  (eachKW, kw[eachKW])
... 
>>> newfoo( 'wolf' 3 'projects' , freud = 90 , gamble = 96 )
arg1  is : wolf
arg2  is 3
additional non - keyword arg: projects
additional keyword arg  'gamble' 96
additional keyword arg  'freud' 90


(3)调用带有可变长参数对象函数

        指的是,将非关键字可变长参数作为元组、关键字可变长参数作为字典来对函数进行调用,使用的函数如下:

1
2
3
4
5
6
7
8
>>>  def  newfoo(arg1, arg2,  * nkw,  * * kw):
...      print  'arg1 is:' , arg1
...      print  'arg2 is:' , arg2
...      for  eachNKW  in  nkw:
...              print  'additional non-keyword arg:' , eachNKW
...      for  eachKW  in  kw.keys():
...              print  "additional keyword arg '%s': %s"  %  (eachKW, kw[eachKW])
...
  • 直接在参数列表中创建元组和字典

1
2
3
4
5
6
7
>>> newfoo( 2 4 * ( 6 8 ),  * * { 'foo' 10 'bar' 12 })
arg1  is 2
arg2  is 4
additional non - keyword arg:  6
additional non - keyword arg:  8
additional keyword arg  'foo' 10
additional keyword arg  'bar' 12
  • 先定义元组和字典对象,再对函数进行调用

1
2
3
4
5
6
7
8
9
10
11
12
>>> aTuple  =  ( 6 7 8 )
>>> aDict  =  { 'z' 9 }
>>> newfoo( 1 2 3 , x = 4 , y = 5 * aTuple,  * * aDict)
arg1  is 1
arg2  is 2
additional non - keyword arg:  3
additional non - keyword arg:  6
additional non - keyword arg:  7
additional non - keyword arg:  8
additional keyword arg  'y' 5
additional keyword arg  'x' 4
additional keyword arg  'z' 9

        不过还有一点需要注意的是,因为还有额外的非关键字参数'3'以及'x'和'y'关键字对,但它们不是'*'和'**'的可变参数中的元素,所以aTuple和aDict参数仅仅是被调函数中最终接收的元组和字典的子集。




7.函数式编程


        Python并不是一种函数式编程语言,但是它支持函数式编程语言的构建,主要是提供lambda表达式和四种内建函数的形式来支持这种特性。

关于函数式编程,借用liao Python的解释:

    函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。

    函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!

    Python对函数式编程提供部分支持。由于Python允许使用变量,因此,Python不是纯函数式编程语言。

        关于Python函数式编程的特点,需要通过对lambda表达式及四种内建函数的理解才能有所体会。


(1)匿名函数与lambda

        在Python中可以使用lambda关键字来创建匿名函数,语法如下:

1
lambda  [arg1[, arg2, ... argN]]: expression

匿名函数:

所谓匿名函数,即是没有名字的函数,在Python中,该函数调用完毕后(其实是在创建声明该匿名函数的过程中同时完成调用),就会被Python解释器通过垃圾回收机制进行回收,因为此时它的引用计数已经为0了。

        一个完整的lambda“语句”代表了一个表达式,这个表达式的定义体必须和声明放在同一行,同时,参数是可选的,如果使用参数的话,通常参数也是表达式的一部分。

lambda表达式返回可调用的函数对象:

用合适的表达式调用一个lambda生成一个可以像其他函数一样使用的函数对象。它们可以被传给其他函数,用额外的引用别名化,作为容器对象以及作为可调用的对象被调用(如果需要的话,可以带参数)。当被调用的时候,如果给定相同的参数的话,这些对象会生成一个和相同表达式等价的结果。

  • lambda表达式可以和单行的函数等价

1
2
3
4
>>>  def  true():  return  True
... 
>>>  lambda True
<function < lambda > at  0x7f8112645de8 >

        不过不同的是,单行的函数在定义后还是存在的,因为有引用,但是lambda表达式生成的函数对象就会马上被回收,因为没有被引用。

  • 使用默认和可变的参数

        lambda表达式:

1
2
lambda  x, y = 2 : x + y
lambda  * z: z

        等价于:

1
2
def  usuallyAdd2(x, y = 2 ):  return  x + y
def  showAllAsTuple( * z):  return  z

        可以做如下测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
>>> a  =  lambda  x, y = 2 : x + y
>>> a( 3 )
5
>>> a( 3 5 )
8
>>> 
>>> b  =  lambda  * z: z
>>> b( 23 'xyz' )
( 23 'xyz' )
>>> b( 42 )
( 42 ,)
>>> 
>>> c  =  lambda  * * kwargs: kwargs
>>> c(x = 3 , y = 4 , z = 5 )
{ 'y' 4 'x' 3 'z' 5 }
>>> d  =  lambda  * args,  * * kwargs: (args, kwargs)
>>> d( 3 4 5 , x = 'abc' )
(( 3 4 5 ), { 'x' 'abc' })

        

        所以从上面的分析中就不难看出,其实lambda使用起来就像是一个函数,只是你可以选择保存或不保存它所返回的函数对象。


(2)内建函数apply()、filter()、map()和reduce()

        这些函数提供了Python函数式编程的特性,而lambda函数可以很好地和使用了这些函数的应用程序结合起来,因为它们都带了一个可执行的函数对象,lambda表达式提供了迅速创建这些函数的机制。

        如下:

函数式编程的内建函数
内建函数 描述
apply(func[, nkw][, kw])

用可选的参数来调用func,nkw为非关键字参数,kw为关键字参数;返回值是函数调用的返回

(该函数已经不建议使用)

filter(func, seq)

调用一个布尔函数func来迭代遍历每个seq中的元素;返回一个使func返回值为true的元素的序列

(可以通过列表的综合使用来替代)

map(func, seq1[, seq2...]) 将函数func作用于给定序列(s)的每个元素,并用一个列表来提供返回值;如果func为None,func表现为一个身份函数,返回一个含有每个序列中元素集合的n个元组的列表
reduce(func, seq[, init]) 将二元函数作用二seq序列的元素,每次携带一对(先前的结果以及下一个序列元素),连续地将现有的结果和下一个值作用在获得的随后的结果上,最后减少序列为一个单一的返回值;如果初始值init给定,第一个比较会是init和第一个序列元素而不是序列的头两个元素


  • apply(func[, nkw][, kw])

        目前已经不再建议使用,因为现在的函数本来就支持参数为可变参数。

  • filter(func, seq)

        使用filter()内建函数可以实现过滤的功能,其工作原理类似如下:

1
2
3
4
5
6
def  filter (bool_func, seq):
     filtered_seq  =  []
     for  eachItem  in  seq:
         if  bool_func(eachItem):
             filtered_seq.append(eachItem)
     return  filtered_seq

        下面通过多次重构一个程序来说明filter()的用法,同时也说明它完全可以用列表的综合应用替代,如下:

  1. 返回一个序列中的奇数

1
2
3
4
5
6
7
8
9
10
#!/usr/bin/env python
from  random  import  randint
 
def  odd(n):
     return  %  2
 
allNums  =  []
for  eachNum  in  range ( 9 ):
     allNums.append(randint( 1 99 ))
print  filter (odd, allNums)

        执行如下:

1
2
/ usr / bin / python2. 7  / home / xpleaf / PycharmProjects / Python_book / 11 / test.py
[ 65 99 31 59 ]
  1. 第一次重构:使用lambda表达式

1
2
3
4
5
6
7
#!/usr/bin/env python
from  random  import  randint
 
allNums  =  []
for  eachNum  in  range ( 9 ):
     allNums.append(randint( 1 99 ))
print  filter ( lambda  n: n % 2 , allNums)

        执行如下:

1
2
/usr/bin/python2 .7  /home/xpleaf/PycharmProjects/Python_book/11/test .py
[59, 51, 17, 15]
  1. 第二次重构:通过列表的综合使用替换filter()

1
2
3
4
5
6
7
#!/usr/bin/env python
from  random  import  randint
 
allNums  =  []
for  eachNum  in  range ( 9 ):
     allNums.append(randint( 1 99 ))
print  [n  for  in  allNums  if  n % 2 ]

        执行如下:

1
2
/usr/bin/python2 .7  /home/xpleaf/PycharmProjects/Python_book/11/test .py
[85, 93, 7, 91, 87, 63]
  1. 第三次重构:进一步简化代码

1
2
3
4
#!/usr/bin/env python
from  random  import  randint as ri
 
print  [n  for  in  [ri( 1 99 for  in  range ( 9 )]  if  n % 2 ]

        执行如下:

1
2
/ usr / bin / python2. 7  / home / xpleaf / PycharmProjects / Python_book / 11 / test.py
[ 93 9 43 85 ]
  • map(func, seq1[, seq2...])

        将函数func并行(如果存在多个序列的话)作用(迭代)于每一个序列中的每一个元素,将所得结果保存在一个列表中,如下:

  1. 单序列

        单序列的map函数的工作原理类似如下:

1
2
3
4
5
def  map (func, seq):
     mapped_seq  =  []
     for  eachItem  in  seq:
         mapped_seq.append(func(eachItem))
     return  mapped_seq

        举例如下:

1
2
3
4
>>>  map ( lambda  x:x + 2 , [ i  for  in  range ( 6 )])
[ 2 3 4 5 6 7 ]
>>>  map ( lambda  x: x * * 2 range ( 6 ))
[ 0 1 4 9 16 25 ]

        可以看到,lambda表达式这里就有非常大的作用了,即不需要特别定义一个函数,只需要用lambda表达式生成一个匿名函数就可以了。

        不过从功能上分析,单序列的map()函数功能完全可以用列表解析实现:

1
2
3
4
>>> [x + 2  for  in  range ( 6 )]
[ 2 3 4 5 6 7 ]
>>> [x * * 2  for  in  range ( 6 )]
[ 0 1 4 9 16 25 ]
  1. 多个序列

        多个序列的map()函数应用就比较灵活了,其工作原理可以查看书上的图解,不过从下面的例子中也可以很容易知道:

1
2
3
4
5
6
7
>>>  map ( lambda  x, y: x  +  y, [ 1 3 5 ], [ 2 4 6 ])
[ 3 7 11 ]
>>> 
>>>  map ( lambda  x,y: (x + y, x - y), [ 1 3 5 ], [ 2 4 6 ])
[( 3 - 1 ), ( 7 - 1 ), ( 11 - 1 )]
>>>  map ( None , [ 1 3 5 ], [ 2 4 6 ])
[( 1 2 ), ( 3 4 ), ( 5 6 )]

        最后一个例子,函数部分使用None,可以用来连接两个序列,并组成一个新的序列,当然,因为有了zip()函数,就可以直接使用zip()函数来实现了:

1
2
>>>  zip ([ 1 3 5 ], [ 2 4 6 ])
[( 1 2 ), ( 3 4 ), ( 5 6 )]
  • reduce(func, seq[ ,init])

        reduce()函数通过取出序列的头两个元素,将它们传入二元函数来获得一个单一的值来实现。然后又用这个值和序列的下一个元素来获得又一个值,之后继续直到整个序列的内容都遍历完毕以及最后的值被算出来为止。

        因此,reduce()的工作原理类似如下:

1
2
3
4
5
6
7
8
9
def  reduce (bin_func, seq, init = None ):
     Iseq  =  list (seq)
     if  init  is  None :
         res  =  Iseq.pop( 0 )
     else :
         res  =  init
     for  item  in  Iseq:
         res  =  bin_func(res, item)
     return  res

        不使用reduce()时,可以用下面的方法计算一个序列中元素的和:

1
2
3
4
5
6
7
8
9
>>>  def  mySum(x, y):  return  x + y
... 
>>> allNums  =  range ( 5 )
>>> total  =  0
>>>  for  eachNum  in  allNums:
...     total  =  mySum(total, eachNum)
... 
>>>  print  'the total is:' , total
the total  is 10

        但是如果使用reduce()函数和lambda表达式,只需要一行代码就可以了:

1
2
>>>  print  'the total is:' reduce ( lambda  x, y: x + y,  range ( 5 ))
the total  is 10

        相当于reduce()函数运行了如下的算术操作:

1
( ( (0 + 1) + 2) + 3) + 4) = 10


(3)偏函数应用

        所谓偏函数,其实就是把一个函数的某个参数固定下来,虽然可以通过函数的默认参数来实现这个功能,但是使用偏函数会方便很多。

  • 简单的函数式例子

        operator模块中有add和mul两个函数,分别实现两个数相加和相乘的功能,如下:

1
2
3
4
5
>>>  from  operator  import  add, mul
>>> add( 1 3 )
4
>>> mul( 100 5 )
500

        但是如果每次都需要相加或相乘同一个数,这样的话就需要每次给add和mul传递两个参数,会比较麻烦。使用functools模块中的partial函数,就可以把一个函数中的某个参数固定下来,这样在下次调用这个函数时,就可以省略一个参数了,如下:

1
2
3
4
5
6
7
8
9
10
11
>>>  from  functools  import  partial
>>> add1  =  partial(add,  1 )             # add1(x) == add(1, x)
>>> mul100  =  partial(mul,  100 )    # mul100(x) == mul(100, x)
>>> add1( 10 )
11
>>> add1( 1 )
2
>>> mul100( 100 )
10000
>>> mul100( 500 )
50000

        这样的话就会方便很多。

  • 警惕关键字

        需要注意的是,固定下来的参数,如果没有指定关键字,默认是固定在原来函数的最左边的,可以先看int()工厂函数的例子:

1
2
3
4
>>> baseTwo  =  partial( int , base = 2 )
>>> baseTwo.__doc__  =  'Convert base 2 string to an int.'
>>> baseTwo( '10010' )
18

        即上面的例子使用了int()内建函数并将base固定为2来指定二进制字符串转化,但如果不指定base关键字时,就会出错:

1
2
3
4
5
>>> baseTwoBAD  =  partial( int 2 )     # baseTwoBAD(x) == int(2, x)
>>> baseTwoBAD( '10010' )     # int(2, '10010')
Traceback (most recent call last):
   File  "<stdin>" , line  1 in  <module>
TypeError: an integer  is  required

        那是因为如果不指定关键字参数,则固定的参数是放到原来函数最左边的,显然如果int()函数的第一个参数为一个数字时就肯定会有异常。




8.变量作用域


(1)全局变量与局部变量

        定义在函数内部的局部变量形成局部作用域,而定义在模块中的全局变量形成全局作用域,但不管局部变量还是全局变量,都存放在它们对应的名称空间中(局部名称空间和全局名称空间)。

        在访问或使用一个变量时,会按照下面的顺序来搜索该变量:

  • 在局部作用域中寻找

  • 在全局作用域中寻找

        因此,可以通过创建局部变量来覆盖全局变量。


(2)global语句

        通过使用global语句,可以在函数内声明一个全局变量,通过这样的方式,就可以在函数内修改全局变量,举例如下:

1
2
3
4
5
6
7
8
9
10
11
>>> is_this_global  =  'xyz'
>>>  def  foo():
...      global  is_this_global
...     this_is_local  =  'abc'
...     is_this_global  =  'def'
...      print  this_is_local  +  is_this_global
... 
>>> foo()
abcdef
>>>  print  is_this_global
def


(3)闭包

        如果在一个内部函数里,对在外部作用域(但不是在全局作用域 )的变量进行引用,那么内部函数就被认为是闭包(closure),定义在外部函数内的但由内部函数引用或者使用的变量就被称为自由变量。

        闭包是一个函数,只是它们能携带一些额外的作用域,它们所引用的上层函数的变量,既不属于全局名称空间,也不属于这个内部函数的局部名称空间,而是属于其它名称空间,因此说闭包带着“流浪”的作用域。

  • 用闭包实现模拟计数器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>>  def  counter(start_at = 0 ):
...     count  =  [start_at]         # 自由变量
...      def  incr():                      # 闭包函数
...             count[ 0 + =  1
...              return  count[ 0 ]
...      return  incr
... 
>>> count  =  counter( 5 )
>>>  print  count()
6
>>>  print  count()
7
>>> count2  =  counter( 100 )
>>>  print  count2()
101
>>>  print  count()
8

        这在想要通过函数修改一个变量,但这个变量又不能是全局变量的时候会很有用,在JavaScript中,其实现方法如下:

1
2
3
4
5
6
7
8
9
10
var  add = ( function  () {
     var  counter = 0;
     return  function  () { return  counter += 1;}
})();
 
add();
add();
add();
 
// 计数器为 3

        不管是Python还是JavaScript,其实原理都是一样的。        

  • 追踪闭包词法的变量

        使用函数的func_closure属性可以追踪一个函数的自由变量,举例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#!/usr/bin/env python
 
output  =  '<int %r id=%#0x val=%d>'
=  = =  =  1
 
 
def  f1():
     =  =  =  1     # 显然f1.func_closure不会追踪到闭包变量
 
     def  f2():
         =  =  3     # f2.func_closure会追踪到x闭包变量,因为在f3()中使用了该变量
                           # 虽然f3也有使用y,但对于f2()来说,y是局部变量,不是闭包变量
                           # 至于f3()使用的w和z,则分别为全局变量和f3的局部变量
         def  f3():
             =  4             # f3.func_closure()会追踪到x和y两个闭包变量
             print  output  %  ( 'w' id (w), w)
             print  output  %  ( 'x' id (x), x)
             print  output  %  ( 'y' id (y), y)
             print  output  %  ( 'z' id (z), z)
 
         clo  =  f3.func_closure
         if  clo:
             print  'f3 closure vars:' , [ str (c)  for  in  clo]
         else :
             print  'no f3 closure vars'
 
         f3()
 
     clo  =  f2.func_closure
     if  clo:
         print  'f2 closure vars:' , [ str (c)  for  in  clo]
     else :
         print  'no f2 closure vars'
     f2()
 
clo  =  f1.func_closure
if  clo:
     print  'f1 closure vars:' , [ str (c)  for  in  clo]
else :
     print  'no f1 closure vars'
f1()

        执行如下:

1
2
3
4
5
6
7
8
/usr/bin/python2 .7  /home/xpleaf/PycharmProjects/Python_book/11/closure_test .py
no f1 closure vars
f2 closure vars: [ '<cell at 0x7fb3a30de440: int object at 0xfa6140>' ]
f3 closure vars: [ '<cell at 0x7fb3a30de440: int object at 0xfa6140>' '<cell at 0x7fb3a30de478: int object at 0xfa6128>' ]
<int  'w'  id =0xfa6158 val=1>
<int  'x'  id =0xfa6140 val=2>
<int  'y'  id =0xfa6128 val=3>
<int  'z'  id =0xfa6110 val=4>

        可以知道,func_closure的工作原理如下:

如果f2()使用了任何的定义在f1()作用域的变量,比如说,非全局的和非f2()的局部域的,那么它们便是自由变量,将会被f2.func_closure追踪到。

        另外可以看到对自由变量的引用是存放在一个称为"cell"的单元对象里,单元是作用域结束后使自由变量的引用存活的一种基础方法:

假设函数f3()已经被传入到其他一些函数,这样便可在稍后,甚至是f2()完成之后调用它。你不想让f2()的栈出现,因为即使我们仅仅在乎f3()使用的自由变量,栈也会让所有的f2()的变量保持存活。单元维持信自由变量以便f2()的剩余部分能被释放掉。

        而对于代码中的注释,一种好的解释方法如下:

在f3()、f2()或f1()中都是找不到变量w的,因为这是个全局变量;在f3()或者f2()中,找不到变量x,因为它是来自f1()的闭包变量;同样,y是一个来自f2()的闭包变量;最后,z是f3()的局部变量。
  • 高级闭包和装饰器的例子

        来看一个综合运用了闭包和装饰器的例子,主要是用来选择是先写入日志再打印还是先打印再写入日志的功能,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#!/usr/bin/env python
# coding: utf-8
from  time  import  time
 
 
def  logged(when):
 
     def  log(f,  * args,  * * kargs):     #日志写入函数,在这个例子中只是打印出来
         print  '''Called:
function: %s
args: %r
kargs: %r'''  %  (f, args, kargs)
 
     def  pre_logged(f):       # 先写入日志,再打印出来(这是一个闭包函数)
         def  wrapper( * args,  * * kargs):
             log(f,  * args,  * * kargs)
             return  f( * args,  * * kargs)
         return  wrapper
 
     def  post_logged(f):      # 先打印出来,再写入日志(这是一个闭包函数)
         def  wrapper( * args,  * * kargs):
             now  =  time()
             try :
                 return  f( * args,  * * kargs)
             finally :
                 log(f,  * args,  * * kargs)
                 print  'time delta: %s'  %  (time()  -  now)
         return  wrapper
 
     try :
         return  { 'pre' : pre_logged,
                 'post' : post_logged}[when]
     except  KeyError, e:
         raise  ValueError(e),  'must be "pre" or "post"'
 
 
@logged ( 'pre' )
def  hello(name):
     print  'Hello,' , name
 
hello( 'World!' )
print  '=' * 50
 
 
@logged ( 'post' )
def  hello(name):
     print  'Hello,' , name
 
hello( 'clyyh' )

        执行如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
/usr/bin/python2 .7  /home/xpleaf/PycharmProjects/Python_book/11/decorator_closure .py
Called:
function : < function  hello at 0x7f7f0e145d70>
args: ( 'World!' ,)
kargs: {}
Hello, World!
==================================================
Hello, clyyh
Called:
function : < function  hello at 0x7f7f0e145e60>
args: ( 'clyyh' ,)
kargs: {}
time  delta: 1.00135803223e-05

        其实只要前面深入理解了装饰器和闭包的知识,相信这个例子理解起来就会很容易。


(4)作用域和lambda

        Python的lambda匿名函数遵循和标准函数一样的作用域规则,一个lambda表达式定义了新的作用域,因为它的局部变量也存放在它的局部名称空间中,在函数内部使用lambda表达式时,可以把其作为一个嵌套函数来看待,只是这个函数没有名字而已。

        所以,下面的函数:

1
2
3
4
5
6
7
8
>>> x  =  10
>>>  def  foo():
...     y  =  5
...     bar  =  lambda :x + y
...      print  bar()
... 
>>> foo()
15

        就等价于:

1
2
3
4
5
6
7
8
>>> x  =  10
>>>  def  foo():
...     y  =  5
...      def  bar():  return  x + y
...      print  bar()
... 
>>> foo()
15

        不过需要了解的是,在Python2.1之前,一个内部函数只能访问两个作用域:它自己的局部作用域和全局的作用域。


(5)变量作用域和名称空间

        变量作用域指的是变量可以使用的范围,而名称空间则是一种数据结构,在这个范围内,这个作用域的变量应该是保存在这个名称空间中。

        可以看下面的一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/usr/bin/env python
 
j, k  =  1 2
 
 
def  proc1():
 
     j, k  =  3 4
     print  'j == %d and k == %d'  %  (j, k)
     =  5
 
 
def  proc2():
 
     =  6
     proc1()
     print  'j == %d and k == %d'  %  (j, k)
 
 
=  7
proc1()
print  'j == %d and k == %d'  %  (j, k)
 
=  8
proc2()
print  'j == %d and k == %d'  %  (j, k)

        执行如下:

1
2
3
4
5
6
/usr/bin/python2 .7  /home/xpleaf/PycharmProjects/Python_book/11/scope_namespace .py
j == 3 and k == 4
j == 1 and k == 7
j == 3 and k == 4
j == 6 and k == 7
j == 8 and k == 7

        其实只要理解了作用域的概念以及变量的搜索顺序,这个例子的输出也就非常好理解了。

        关于作用域的问题,还可以看下面的一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env python
 
global_var  =  'abc'
 
def  test1():
     =  '123'
 
     def  test2():
         =  '456'
         print  'test2 <local vars>:' locals ()
 
     print  'test1 <local vars>:' locals ()
     test2()
 
 
test1()

        执行如下:

1
2
3
/ usr / bin / python2. 7  / home / xpleaf / PycharmProjects / Python_book / 11 / test.py
test1 <local  vars >: { 'a' '123' 'test2' : <function test2 at  0x7f025030ec80 >}
test2 <local  vars >: { 'b' '456' }

        既然test2()可以使用test1()中的变量,locals()中没有a呢?原因如下:

  • locals()存放的是局部变量的名称空间

  • test2()可以使用test1()中的变量,那是闭包的概念




9.递归


        直接看下面一个用递归实现阶乘运算的例子:

1
2
3
4
5
6
7
8
9
10
>>>  def  factorial(n):
...      if  = =  0  or  = =  1 :
...              return  1
...      else :
...              return  n * factorial(n - 1 )
... 
>>> factorial( 3 )
6
>>> factorial( 10 )
3628800




10.生成器


  • 协同程序的概念

        协同程序是可以运行的独立函数调用,可以暂停或者挂起,并从程序离开的地方继续或者重新开始。在调用者和(被调用的)协同程序也有通信。举例来说,当协同程序暂停的时候,我们能从其中获得一个中间的返回值,当调用回到程序中时,能够传入额外或者改变了的参数,但仍能够从我们上次离开的地方继续,并且所有状态完整。挂起返回出中间值并多次继续的协同程序被称为生成器,那就是Python的生成器真正在做的事。

  • Python式的生成器

        从语法上讲,生成器是一个带yield语句的函数。一个函数或者子程序只返回一次,但一个生成器能暂停执行并返回一个中间的结果——那就是yield语句的功能,返回一个值给调用者并暂停执行(即yield产生一个结果并返回给调用者)。当生成器的next()方法被调用的时候,它会准确地从离开的地方继续。


(1)简单的生成器特性

        与迭代器相似,生成器以类似的方式来运作:没调用next()方法时,如果没有更多的值返回,就会抛出一个StopIteration异常。

        下面是一个生成器函数:

1
2
3
4
5
6
>>>  def  simpleGen():
...      yield  1
...      yield  2
... 
>>>  type (simpleGen)
< type  'function' >

        用该生成器函数可以获得和保存一个生成器对象:

1
2
3
4
5
6
7
8
9
10
11
>>> myG  =  simpleGen()
>>>  type (myG)
< type  'generator' >
>>> myG. next ()
1
>>> myG. next ()
2
>>> myG. next ()
Traceback (most recent call last):
   File  "<stdin>" , line  1 in  <module>
StopIteration

        当然也可以用for循环来自动处理next和StopIteration,如下:

1
2
3
4
5
6
>>> myG  =  simpleGen()
>>>  for  item  in  myG:
...      print  item
... 
1
2

        只不过如果想要再次使用的话就需要重新生成一个生成器对象,当然,可以用for直接处理生成器函数:

1
2
3
4
5
>>>  for  eachItem  in  simpleGen():
...      print  eachItem
... 
1
2


(2)加强的生成器特性

        除了可以使用next()来获得下个生成的值,还可以使用send()将值回送给生成器,在生成器中抛出异常(使用generator.throw(ErrorType)),以及要求生成器退出(使用close())。

        由于双向的动作需要使用send()来向生成器发送值,同时还需要生成器返回(产生,yield)一个值,所以yield语句必须是一个表达式。举例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
>>>  def  counter(start_at = 0 ):
...     count  =  start_at
...      while  True :
...             val  =  ( yield  count)
...              if  val  is  not  None :
...                     count  =  val
...              else :
...                     count  + =  1
... 
>>> mycount  =  counter( 5 )
>>> mycount. next ()
5
>>> mycount. next ()
6
>>> mycount.send( 9 )
9
>>> mycount. next ()
10
>>> mycount.close()
>>> mycount. next ()
Traceback (most recent call last):
   File  "<stdin>" , line  1 in  <module>
StopIteration

        每一次next()的执行,都会yield出一个值并返回,同时保持原来函数的执行状态,当执行下一次next()时,继续上一次yield语句的下一条语句,然后直到遇到下一个yield语句并返回一个值,以此类推。




本文转自 xpleaf 51CTO博客,原文链接:http://blog.51cto.com/xpleaf/1763071,如需转载请自行联系原作者
相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
[oeasy]python091_列表_索引_index_中括号_索引函数
本文介绍了Python中列表与字符串的索引及index函数用法。通过range生成列表,使用索引[]访问和修改列表元素,index函数查找元素位置。字符串支持索引访问但不可直接修改。还探讨了16进制数在Python中的表示方法,以及日期、月份等特殊字符的Unicode范围。最后总结了列表与字符串操作的区别,并预告后续内容,提供蓝桥云课、GitHub和Gitee链接供进一步学习。
42 20
|
21天前
|
[oeasy]python086方法_method_函数_function_区别
本文详细解析了Python中方法(method)与函数(function)的区别。通过回顾列表操作如`append`,以及随机模块的使用,介绍了方法作为类的成员需要通过实例调用的特点。对比内建函数如`print`和`input`,它们无需对象即可直接调用。总结指出方法需基于对象调用且包含`self`参数,而函数独立存在无需`self`。最后提供了学习资源链接,方便进一步探索。
56 17
[oeasy]python083_类_对象_成员方法_method_函数_function_isinstance
本文介绍了Python中类、对象、成员方法及函数的概念。通过超市商品分类的例子,形象地解释了“类型”的概念,如整型(int)和字符串(str)是两种不同的数据类型。整型对象支持数字求和,字符串对象支持拼接。使用`isinstance`函数可以判断对象是否属于特定类型,例如判断变量是否为整型。此外,还探讨了面向对象编程(OOP)与面向过程编程的区别,并简要介绍了`type`和`help`函数的用法。最后总结指出,不同类型的对象有不同的运算和方法,如字符串有`find`和`index`方法,而整型没有。更多内容可参考文末提供的蓝桥、GitHub和Gitee链接。
45 11
Python中main函数:代码结构的基石
在Python中,`main`函数是程序结构化和模块化的重要组成部分。它实现了脚本执行与模块导入的分离,避免全局作用域污染并提升代码复用性。其核心作用包括:标准化程序入口、保障模块复用及支持测试驱动开发(TDD)。根据项目复杂度,`main`函数有基础版、函数封装版、参数解析版和类封装版四种典型写法。 与其他语言相比,Python的`main`机制更灵活,支持同一文件作为脚本运行或模块导入。进阶技巧涵盖多文件项目管理、命令行参数处理、环境变量配置及日志集成等。此外,还需注意常见错误如全局变量污染和循环导入,并通过延迟加载、多进程支持和类型提示优化性能。
44 0
Python入门:8.Python中的函数
### 引言 在编写程序时,函数是一种强大的工具。它们可以将代码逻辑模块化,减少重复代码的编写,并提高程序的可读性和可维护性。无论是初学者还是资深开发者,深入理解函数的使用和设计都是编写高质量代码的基础。本文将从基础概念开始,逐步讲解 Python 中的函数及其高级特性。
Python入门:8.Python中的函数
Python高级编程与实战:深入理解函数式编程与元编程
本文深入介绍Python的函数式编程和元编程。函数式编程强调纯函数与不可变数据,涵盖`map`、`filter`、`reduce`及`lambda`的使用;元编程则涉及装饰器、元类和动态属性等内容。通过实战项目如日志记录器和配置管理器,帮助读者掌握这些高级技术,编写更灵活高效的Python程序。
Python学习:内建属性、内建函数的教程
本文介绍了Python中的内建属性和内建函数。内建属性包括`__init__`、`__new__`、`__class__`等,通过`dir()`函数可以查看类的所有内建属性。内建函数如`range`、`map`、`filter`、`reduce`和`sorted`等,分别用于生成序列、映射操作、过滤操作、累积计算和排序。其中,`reduce`在Python 3中需从`functools`模块导入。示例代码展示了这些特性和函数的具体用法及注意事项。
|
3月前
|
Python中的round函数详解及使用示例
`round()`函数是Python内置的用于四舍五入数字的工具。它接受一个数字(必需)和可选的小数位数参数,返回最接近的整数或指定精度的浮点数。本文详细介绍其用法、参数及示例,涵盖基本操作、负数处理、特殊情况及应用建议,帮助你更好地理解和运用该函数。
180 2
[oeasy]python069_当前作用域都有些什么_列表dir_函数_builtins
本文介绍了Python中`dir()`函数的使用方法及其作用。`dir()`可以列出当前作用域内的所有变量和成员,类似于`locals()`,但`dir()`不仅限于本地变量,还能显示模块中的所有成员。通过`dir(__builtins__)`可以查看内建模块中的所有内建函数,如`print`、`ord`、`chr`等。此外,还回顾了`try-except-finally`结构在数据库连接中的应用,并解释了为何`print`函数可以直接使用而无需导入,因为它位于`__builtins__`模块中。最后,简要提及了删除`__builtins__.print`的方法及其影响。
60 1
利用Python内置函数实现的冒泡排序算法
在上述代码中,`bubble_sort` 函数接受一个列表 `arr` 作为输入。通过两层循环,外层循环控制排序的轮数,内层循环用于比较相邻的元素并进行交换。如果前一个元素大于后一个元素,就将它们交换位置。
179 67