python一些语法总结。
Python语法
高阶函数
既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
匿名函数lambda
关键字lambda表示匿名函数,冒号前面的x表示函数参数。
匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。
用匿名函数有个好处,因为函数没有名字,不必担心函数名冲突。此外,匿名函数也是一个函数对象.map有两个参数,第一个是函数对象,第二个是一个可迭代对象。map将函数依次作用于序列的每一个元素。返回一个迭代器(不能直接用print输出)。
reduce函数位于
functools
模块中,同样接受两个参数,一个函数对象一个可迭代序列,和map不同之处在于reduce处理完一个元素会将结果累计至下一个元素。from functools import reduce
def fn(x,y): return 10*x+y L=[i for i in range(10)] res1=reduce(fn,L)
上述代码计算了1~9的累加。
def fn(x,y): return 10*x+y
def char_to_num(s): return {'0':0,'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9}[s] print(reduce(fn,map(char_to_num,'123456')))
上述代码实现了字符串转化为整数
CHAR_TO_FLOAT = { '0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9, '.': -1 } def char_to_float(s): nums=map(lambda ch:CHAR_TO_FLOAT[ch],s) point=0 def to_float(x,y): nonlocal point if y==-1: point=1 return x if point==0: return 10*x+y else: point*=10 return x+y/point return reduce(to_float,nums,0.0)
字符串转化为float,reduce有第三个可选参数,作为初始的默认值,从这个默认值开始计算。如果没给且序列只有一个元素,reduce返回第一个元素。
filter()
函数用于过滤序列,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。返回的也是一个迭代器。下面用埃氏筛法求素数
def odd_iter(): #生成一个奇数序列 n=1 while True: n+=2 yield n #过滤函数 def not_divisible(n): return lambda x:x%n>0 def primes(): yield 2 it=odd_iter()#初始化为奇数序列 while True: n=next(it) yield n it=filter(not_divisible(n),it)#每次过滤掉n的倍数
sorted()
函数可以对序列进行排序,若要定制个性化操作,可以传入关键字参数key和reverse等例如key=abs就是按绝对值排序,key=str.lower就是把原始序列变成小写再排序即忽略大小写
高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。返回一个函数时,牢记该函数并未执行,返回函数中不要引用任何可能会变化的变量。相关参数和变量都保存在返回的函数中——闭包(Closure)的程序结构,返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
装饰器
在代码运行过程中不改变函数定义而动态增加函数功能的特性,称为装饰器
(Decorator)。
函数对象有个name属性可以得到函数对象的名字。
本质上,decorator就是一个返回函数的高阶函数。
看以下代码:
def log(func):
@functools.wraps(func)
def wrapper(*args,**kargs):
print('call %s' % func.__name__)
return func(*args,**kargs)
return wrapper
@log
def now():
print('2017-2-18')
加上@log
之后,相当于执行了now = log(now)
,now不再指向原来的函数,而是log函数中返回的wrapper函数。wrapper()
函数的参数定义是(*args, **kwargs)
,因此,wrapper()函数可以接受任意参数的调用。
如果装饰器本身就需要参数,那就得套三层了。也就是说第一个函数返回装饰器,如下:
def log(text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
@log('execute')
def now():
print('2017-2-18')
经过装饰之后的函数属性会变化,如name,@functools.wraps(func)
语句用于修正。
在面向对象(OOP)的设计模式中,decorator被称为装饰模式。OOP的装饰模式需要通过继承和组合来实现,而Python除了能支持OOP的decorator外,直接从语法层次支持decorator。Python的decorator可以用函数实现,也可以用类实现。
slots以及运算符重载
python中当创建一个类或者实例之后,均可以再给它添加方法或者属性
给类添加方法和属性只需要用类名.属性或方法名=属性或方法
即可,给实例添加属性也类似,只是给实例添加方法时麻烦点:
class student:
pass
s=student()
from types import MethodType
s.age=MethodType(age,s)
s.age(25)
可以在定义类时利用slots属性设置可以添加的属性。比如,上述student类:
class student(object):
__slots__=('name','age')
这样的话就不能添加别的属性了。
使用_slots_要注意,_slots_定义的属性仅对当前类实例起作用,对继承的子类是不起作用的。
除非在子类中也定义_slots_,这样,子类实例允许定义的属性就是自身的_slots_加上父类的_slots_。
看到类似slots这种形如xxx的变量或者函数名就要注意,这些在Python中是有特殊用途的。
slots我们已经知道怎么用了,还有好多方法。
str和repr
两者的区别是str()返回用户看到的字符串,而repr()返回程序开发者看到的字符串(print调用str,直接在交互式解释器输出调用的是repr),也就是说,repr()是为调试服务的。
但是通常str()和repr()代码都是一样的,所以,有个偷懒的写法:
__repr__ = __str__
如果一个类想被用于for … in循环,类似list或tuple那样,就必须实现一个iter()方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的next()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。
要达到多个迭代器的效果,_iter_只需返回新的对象而不是本身
class Iterator: def __init__(self,data): self.data=data self.index=-1 self.end=len(data) def __iter__(self): """ if iter(self.data) is iter(self.data): print('ha')#返回的并不是对象本身,所以可以使用多个迭代器 return iter(self.data)#iter针对容器会返回迭代器,针对迭代器会返回它本身 """ return self.data #一次性的迭代器 def __next__(self): self.index += 1 if self.index==self.end: raise StopIteration return self.data[self.index] def __back__(self): self.index-=1 if self.index==-1: raise StopIteration return self.data[self.index] class container: def __init__(self,data): self.data=data def __iter__(self): return Iterator(data)
getitem用于索引、分片,setitem索引赋值,delitem索引和分片删除
__getattr__
当调用不存在的属性时,Python解释器会试图调用getattr(self, ‘score’)来尝试获得属性score
class Student(object): def __init__(self): self.name = 'Michael' def __getattr__(self, attr): if attr=='score': return 99
__call__
__call__()还可以定义参数。对实例进行直接调用就好比对一个函数进行调用一样,所以你完全可以把对象看成函数,把函数看成对象,因为这两者之间本来就没啥根本的区别。
通过callable()函数,我们就可以判断一个对象是否是“可调用”对象。
继承、@property和super
还记得装饰器(decorator)可以给函数动态加上功能吗?对于类的方法,装饰器一样起作用。Python内置的@property装饰器就是负责把一个方法变成属性调用的.
@property
把一个getter方法变成属性,只需要加上@property
就可以了。如果还要求属性可写,就再设置setter方法。如下所示:
class Student(object):
def __init__(self,_score):
self.score=_score
@property
def score(self):
return self._score
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
@score.deleter
def score(self):
raise AttributeError("can't delete attribute")
其实,完全可以定义两个函数,set和get来处理,这样操作时调用相应方法即可。装饰器的重要特性就是它看起来像一个普通的属性,但是根据访问它的不同方式会触发不同方法,这使得实例的接口变得非常单一。
这里可以看到,三个相互关联的方法有着相同的名称score
,这个表示的就是属性
(构造函数里的属性赋值也会触发检查),其它两个方法将可选的setter和deleter函数附加到了score属性上。
一个oop的例子
python的继承、封装很好理解,和其它语言如出一辙,对于多态而言,由于python是动态类型的语言,看如下代码:
class Base:
def __init__(self,x):
self.x=x
def method(self):
print('In base')
class Sub(Base):
def __init__(self,x,y):
super().__init__(x)
self.y=y
def method(self):
print('start sub')
Base.method(self)
print('end sub')
x=Base(5)
y=Sub(5,10)
def test(obj):
obj.method()
test(x)
test(y)
print(x.x)
print(y.x,y.y)
输出结果如下:
In base
start sub
In base
end sub
5
5 10
对于静态语言(例如Java)来说,如果需要传入Base类型,则传入的对象必须是Base类型或者它的子类,否则,将无法调用method()方法。
对于Python这样的动态语言来说,则不一定需要传入Base类型。我们只需要保证传入的对象有一个method()方法就可以了,这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。python变量可以引用一切对象,所以多态性更灵活!
注意,这里推荐使用super()
函数调用父类方法而不是直接用父类名(虽然在这个地方效果一样),但是多继承时会有点问题。
super()函数
看以下多继承的例子(python支持多重继承,java只有单继承,c++也是可以多重继承):
class Root(object):
def __init__(self):
print('this is root!')
class A(Root):
def __init__(self):
print('enter A')
super().__init__()
print('exit A')
class B(Root):
def __init__(self):
print('enter B')
super().__init__()
print('exit B')
class C(A,B):
def __init__(self):
print('enter C')
super().__init__()
print('exit C')
c=C()
print(c.__class__.mro())
上述代码的输出如下:
enter C
enter A
enter B
this is root!
exit B
exit A
exit C
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Root'>, <class 'object'>]
super
使用了方法解析顺序
(Method Resolution Order,或MRO),会对这个元组进行依次搜索,这个元组用于确定一个方法是在当前类还是父类即扫描顺序。用类的__mro__属性
或者mro()方法
都可获得这个元组。至于这个元组怎么得到的可以看下面的链接,介绍c3算法:
了解更多mro以及c3算法
下面分析一下上面的代码
由解析顺序可以看到,首先初始化C类,然后由super调用
会调用A类的构造函数,A类又继续调用B类的构造函数,B类最后调用Root类的构造函数。总之,super干的事可以总结为下面的代码:
def super(cls,inst):
mro=inst.__class__.mro()
return mro[mro.index(cls)+1]
第一个参数是确定当前类的位置,第二个参数用于得到mro元组。
也就是返回mro元组的下一个类。这里,super有两个参数,上述代码没添加参数,文档中说了一点:
The zero argument form only works inside a class definition, as the compiler fills in the necessary details to correctly retrieve the class being defined, as well as accessing the current instance for ordinary methods.
大意是说零参数形式仅在类定义中工作,编译器会填充必要的细节。这里也可以加上参数。如下:
class Root(object):
def __init__(self):
print('this is root!')
class A(Root):
def __init__(self):
print('enter A')
super(A,self).__init__()
print('exit A')
class B(Root):
def __init__(self):
print('enter B')
super(B,self).__init__()
print('exit B')
class C(A,B):
def __init__(self):
print('enter C')
super(C,self).__init__()
print('exit C')
结果完全一样的。
其实,super有两个典型的用例。
- 在具有单继承的类层次结构中,super可以用于引用父类而不明确命名它们,从而使代码更易于维护。这种使用与其他编程语言中super的使用非常相似。
- 第二种用例是在动态执行环境中支持协作多重继承。此用例是Python独有的,在静态编译语言或只支持单继承的语言中找不到。这使得可以实现“菱形图”,其中多个基类实现相同的方法。 (如上示例,如果全部不用super而用类名,会发现Root类的构造函数被调用了两次)
现在水平有限,容日后再更吧~/(ㄒoㄒ)/
最后一点,不论是c3还是mro啦都是新式类(new-style class)的特性,如果不是那就( ^_^ )/~~拜拜。