在知乎上看到的问题——python有哪些优雅的代码实现。

下面的代码大概也算不上优雅。

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。

一下代码在python3中实现

更多内容可见:http://book.pythontips.com/en/latest/args_and_kwargs.html

lambda函数的使用

lambda,又称匿名函数。当我们在传入函数时,有些时候,不需要显式地定义函数,直接传入匿名函数更方便。

比如命名一个普通的函数:

1 2 def  f(x):      return  *  x

在这里,f为函数名,x是函数的参数,x*x则是函数的返回结果。

我们可以换成lambda的形式则是:

1 2 3 4 5 >>>  lambda  x : x * x <function < lambda > at  0x7fa2d1298048 > >>> f  =  lambda  x : x * x >>> f( 3 ) 9

 lambda函数有一个限制就是函数中只能有一个表达式(事例中的x*x),该表达式的结果即是返回值。当然这个表达式可以用下面的一些技巧写的更“优雅”一些。

其中lambda函数返回是一个对象,其实在python中,绝大部分的都是对象,函数也是对象。所以我们能将lambda函数赋给其它对象(事例中的f)。但是不建议这么做。一般使用lambda表达式时要注意:

1.逻辑简单,切忌在一个lambda表达式中做出很复杂的逻辑,这么做可能感觉逼格很高但是代码的可读性会变得非常差。

2.一次性使用,就像上面所说的不建议使用f = lambda  x:....的形式

map,reduce,filter函数的使用

map()函数接收两个参数,一个是函数,一个是Iterablemap将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。

比如我们有一个函数f(x)=x2,要把这个函数作用在一个list [1, 2, 3, 4, 5, 6, 7, 8, 9]上,就可以用map()实现如下:

 优雅的python 随笔

1 2 3 4 5 6 >>>  def  f(x): ...      return  *  x ... >>> r  =  map (f, [ 1 2 3 4 5 6 7 8 9 ]) >>>  list (r) [ 1 4 9 16 25 36 49 64 81 ]

 其中注意,传进map()的第一个参数是 f 而不是f(),其中f表示的是f函数对象本身而f()则是对函数f的调用。

map()作为高阶函数,事实上它把运算规则抽象了,因此,我们不但可以计算简单的f(x)=x2,还可以计算任意复杂的函数,比如,把这个list所有数字转为字符串:

1 2 >>>  list ( map ( str , [ 1 2 3 4 5 6 7 8 9 ])) [ '1' '2' '3' '4' '5' '6' '7' '8' '9' ]

 当然,map()中还可以传入lambda表达式:

1 2 >>>  list ( map ( lambda  x: x * x, range ( 10 ))) [ 0 1 4 9 16 25 36 49 64 81 ]

 或者再结合一下:

1 2 >>>  list ( map ( lambda  x:  str (x * x), range ( 10 ))) [ '0' '1' '4' '9' '16' '25' '36' '49' '64' '81' ]

 再看reduce的用法。reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:

1 reduce (f, [x1, x2, x3, x4])  =  f(f(f(x1, x2), x3), x4)

 

比方说对一个序列求和,就可以用reduce实现:

1 2 3 >>>  from  functools  import  reduce >>>  reduce ( lambda  x,y:x + y,[ 1 , 2 , 3 , 4 ]) 10

 

Python内建的filter()函数用于过滤序列。

map()类似,filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。

例如返回一个list中的奇数:

1 2 >>>  list ( filter ( lambda  x:x  %  2  = =  1 , range ( 10 ))) [ 1 3 5 7 9 ]

 

列表推导

列表推导,又称列表生成式,即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式。

举个例子,要生成list [0,1, 2, 3, 4, 5, 6, 7, 8, 9]可以用list(range(10))

1 2 >>>  list ( range ( 10 )) [ 0 1 2 3 4 5 6 7 8 9 ]

 在python2中有点区别,python2中的range()直接生产列表而python3中生产的是一个range对象,需要通过list或者[]来生成。

1 2 >>> [x  for  in  range ( 10 )] [ 0 1 2 3 4 5 6 7 8 9 ]

 或者进阶一点点:

1 2 3 4 >>> [x * for  in  range ( 10 )] [ 0 1 4 9 16 25 36 49 64 81 ] >>> [ str (x)  for  in  range ( 10 )] [ '0' '1' '2' '3' '4' '5' '6' '7' '8' '9' ]
>>> L = ['Hello', 'World', 'IBM', 'Apple'] >>> [s.lower() for s in L] ['hello', 'world', 'ibm', 'apple']

感觉它的写法有点想lambda表达式。

然后其中也可以多几层的嵌套:

1 2 >>> [m  +  for  in  'ABC'  for  in  'XYZ' ] [ 'AX' 'AY' 'AZ' 'BX' 'BY' 'BZ' 'CX' 'CY' 'CZ' ]

 三层和三层以上的循环就很少用到了。

 

yield和generator生成器

简单地讲,yield 的作用就是把一个函数变成一个 generator。

例如使用yield生成裴波那契数列:

1 2 3 4 5 6 7 def  fab( max ):     n, a, b  =  0 0 1     while  n <  max :         yield  b         # print b         a, b  =  b, a  +  b         =  +  1

 

1 2 3 4 5 6 7 8 >>>  for  in  fab( 5 ): ...      print  n ... 1 1 2 3 5

使用yield的好处在于,它返回的是一个generator生成器。类似于python3中的range()和python2中的xrange()。带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator,调用 fab(5) 不会执行 fab 函数,而是返回一个 iterable 对象!在 for 循环执行时,每次循环都会执行 fab 函数内部的代码,执行到 yield b 时,fab 函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。

因为返回的是一个生成器,所以可以使用next()方法进行访问:

1 2 3 4 5 6 7 8 9 10 11 >>> f  =  fab( 5 ) >>>  next (f) 1 >>>  next (f) 1 >>>  next (f) 2 >>>  next (f) 3 >>>  next (f) 5

 generator生成器,前面我们看了列表推导,使用[]进行生成,其中把[]换成(),就创建了一个generator:

1 2 3 4 5 6 >>> L  =  [x  *  for  in  range ( 10 )] >>> L [ 0 1 4 9 16 25 36 49 64 81 ] >>> g  =  (x  *  for  in  range ( 10 )) >>> g <generator  object  <genexpr> at  0x7fd2264607d8 >

 L是一个list,而g是一个generator。其中generator是可迭代的。

 

装饰器

装饰器(Decorator),当我们希望为函数增加功能,但是却不想修改原函数又或者没有权限修改原函数的时候,就需要用到装饰器了。

比如我们有一个函数:

1 2 3 4 5 def  func():      print ( "I have a dream!" ) func()   I have a dream!

 是的,我有一个梦想!现在我们想要知道我什么时候有一个梦想,就是我们需要在执行函数的时候打印时间。那么:

1 2 3 4 5 6 7 8 9 10 11 12 '''定义一个装饰器''' def  log_time(func):      def  wrapper( * args,  * * kw):          print ( "run %s() and time is :"  %  func.__name__ + str (datetime.datetime.now()))          return  func( * args, * * kw)      return  wrapper   @log_time def  func():      print ( "I have a dream!" )   func()

 输出:

1 2 run func()  and  time  is  : 2016 - 10 - 10  14 : 26 : 08.296495 I have a dream!

 本质上,decorator就是一个返回函数的高阶函数。其中,我们给log_tim()传入一个参数,这个参数是一个函数对象,并且返回一个函数对象。然后在其中定义了wrapper(),这两个参数并没有意义,只是为了说明这里面可以传入任意类型的参数。

然后用@语法将其放在函数定义处。其相当于:

1 func  =  log_time(func)

 由于log_time()是一个decorator,返回一个函数,所以,原来的func()函数仍然存在,只是现在同名的func变量指向了新的函数,于是调用func()将执行新函数,即在log_time()函数中返回的wrapper()函数。

在使用装饰器时请注意:

用decorator修饰一个函数得到另一个函数时,原来的那个函数依然是逻辑中心,而decorator所增加的只是相对外围的功能,不能那个什么宾那个什么主。

即使去掉装饰器,整个函数的逻辑仍需完整、清晰。

欢迎多来访问博客:http://liqiongyu.com/blog

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄