魔术方法

魔术方法

使用Python的魔术方法的最大优势在于他们提供了一种简单的方法来让对象可以表现的像内置类型一样。那意味着你可以避免丑陋的,违反直觉的,不标准的的操作方法。
特殊属性
属性
含义
__name__
类、函数、方法等名字
__module__
类定义所在的模块名?
__class__
对象或类所属的类
__bases__
类的基类的元组,顺序为它们在基类列表中出现的顺序
__doc__
类、函数的文档字符串,如果没有定义则为None
__mro__
类的mro,class.mro()返回的结果保存在__mro__中
__dict__
类或实例的属性,可写的字典
查看属性
方法
意义
__dir__
返回类或者对象的所有成员名称列表.dir()函数就是调用__dir__().如果提供了__dir__(),则返回属性的列表,否则就会尽量从__dict__属性中收集信息
  • 如果dir([obj])参数包含方法__dir__(),该方法将被调用。如果参数不包含__dir__(),该方法将最大限度地收集参数信息。
  • dir()对于不同类型的对象具有不同的行为
  • 如果对象是模块对象,列表包含模块的属性名
  • 如果对象是类型或者类对象,列表包含类的属性名,及它的积累的属性名
  • 否则,列表包含对象的属性名,它的类的属性名和类的基类的属性名
import animal
from animal import Animal
class Cat(Animal):
y = ‘cat’
x = ‘abcd’
class Dog(Animal):
def __dir__(self):
return [‘dog’]
print(‘————————‘)
print(‘Current Module\’s name = {}’.format(dir()))#模块名词空间内的属性
print(‘Animal Module\’s name = {}’.format(dir(animal)))#animal模块名词空间的属性
print(“object’s dict={}”.format(sorted(object.__dict__.keys())))#object字典内容
print(“Animal’s dir()={}”.format(dir(Animal)))#类名词空间下的属性方法
print(‘Cat dirr()={}’.format(dir(Cat)))#类Cat的dir()
print(‘————————‘)
tom = Cat(‘tome’)
print(sorted(dir(tom)))#实例tom的属性,Cat类及所有祖先的类属性
print(sorted(tom.__dir__()))#同上
#dir()的等价,近似如下,__dict__字典中几乎包括了所有属性
print(sorted(set(tom.__dict__.keys())|set(Cat.__dict__.keys())|set(object.__dict__.keys())))
print(‘Dog dir = {}’.format(dir(Dog)))
dog = Dog(‘snoppy’)
print(dir(dog))
print(dog.__dict__)
———————————–
{‘_name’: ‘Thunk’, ‘weight’: 10, ‘_Animal__age’: 20}
[‘_Animal__age’, ‘__class__’, ‘__delattr__’, ‘__dict__’, ‘__dir__’, ‘__doc__’, ‘__eq__’, ‘__format__’, ‘__ge__’, ‘__getattribute__’, ‘__gt__’, ‘__hash__’, ‘__init__’, ‘__le__’, ‘__lt__’, ‘__module__’, ‘__ne__’, ‘__new__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__setattr__’, ‘__sizeof__’, ‘__str__’, ‘__subclasshook__’, ‘__weakref__’, ‘_name’, ‘weight’, ‘x’]
————————
Current Module’s name = [‘Animal’, ‘Cat’, ‘Dog’, ‘__builtins__’, ‘__cached__’, ‘__doc__’, ‘__file__’, ‘__loader__’, ‘__name__’, ‘__package__’, ‘__spec__’, ‘animal’]
Animal Module’s name = [‘Animal’, ‘__builtins__’, ‘__cached__’, ‘__doc__’, ‘__file__’, ‘__loader__’, ‘__name__’, ‘__package__’, ‘__spec__’, ‘c’]
object’s dict=[‘__class__’, ‘__delattr__’, ‘__dir__’, ‘__doc__’, ‘__eq__’, ‘__format__’, ‘__ge__’, ‘__getattribute__’, ‘__gt__’, ‘__hash__’, ‘__init__’, ‘__le__’, ‘__lt__’, ‘__ne__’, ‘__new__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__setattr__’, ‘__sizeof__’, ‘__str__’, ‘__subclasshook__’]
Animal’s dir()=[‘__class__’, ‘__delattr__’, ‘__dict__’, ‘__dir__’, ‘__doc__’, ‘__eq__’, ‘__format__’, ‘__ge__’, ‘__getattribute__’, ‘__gt__’, ‘__hash__’, ‘__init__’, ‘__le__’, ‘__lt__’, ‘__module__’, ‘__ne__’, ‘__new__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__setattr__’, ‘__sizeof__’, ‘__str__’, ‘__subclasshook__’, ‘__weakref__’, ‘x’]
Cat dirr()=[‘__class__’, ‘__delattr__’, ‘__dict__’, ‘__dir__’, ‘__doc__’, ‘__eq__’, ‘__format__’, ‘__ge__’, ‘__getattribute__’, ‘__gt__’, ‘__hash__’, ‘__init__’, ‘__le__’, ‘__lt__’, ‘__module__’, ‘__ne__’, ‘__new__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__setattr__’, ‘__sizeof__’, ‘__str__’, ‘__subclasshook__’, ‘__weakref__’, ‘x’, ‘y’]
————————
[‘_Animal__age’, ‘__class__’, ‘__delattr__’, ‘__dict__’, ‘__dir__’, ‘__doc__’, ‘__eq__’, ‘__format__’, ‘__ge__’, ‘__getattribute__’, ‘__gt__’, ‘__hash__’, ‘__init__’, ‘__le__’, ‘__lt__’, ‘__module__’, ‘__ne__’, ‘__new__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__setattr__’, ‘__sizeof__’, ‘__str__’, ‘__subclasshook__’, ‘__weakref__’, ‘_name’, ‘weight’, ‘x’, ‘y’]
[‘_Animal__age’, ‘__class__’, ‘__delattr__’, ‘__dict__’, ‘__dir__’, ‘__doc__’, ‘__eq__’, ‘__format__’, ‘__ge__’, ‘__getattribute__’, ‘__gt__’, ‘__hash__’, ‘__init__’, ‘__le__’, ‘__lt__’, ‘__module__’, ‘__ne__’, ‘__new__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__setattr__’, ‘__sizeof__’, ‘__str__’, ‘__subclasshook__’, ‘__weakref__’, ‘_name’, ‘weight’, ‘x’, ‘y’]
[‘_Animal__age’, ‘__class__’, ‘__delattr__’, ‘__dir__’, ‘__doc__’, ‘__eq__’, ‘__format__’, ‘__ge__’, ‘__getattribute__’, ‘__gt__’, ‘__hash__’, ‘__init__’, ‘__le__’, ‘__lt__’, ‘__module__’, ‘__ne__’, ‘__new__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__setattr__’, ‘__sizeof__’, ‘__str__’, ‘__subclasshook__’, ‘_name’, ‘weight’, ‘x’, ‘y’]
Dog dir = [‘__class__’, ‘__delattr__’, ‘__dict__’, ‘__dir__’, ‘__doc__’, ‘__eq__’, ‘__format__’, ‘__ge__’, ‘__getattribute__’, ‘__gt__’, ‘__hash__’, ‘__init__’, ‘__le__’, ‘__lt__’, ‘__module__’, ‘__ne__’, ‘__new__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__setattr__’, ‘__sizeof__’, ‘__str__’, ‘__subclasshook__’, ‘__weakref__’, ‘x’]
[‘dog’]
{‘_name’: ‘snoppy’, ‘weight’: 10, ‘_Animal__age’: 20}
魔术方法
  • 分类:
    • 创建与销毁
      • __init__与__del__
    • hash
    • bool
    • 可视化
    • 运算符重载
    • 容器和大小
    • 可调用对象
    • 上下文管理
    • 反射
    • 描述器
    • 其他杂项
Hash
方法
意义
__hash__
内建函数hash()调用的返回值,返回一个整数。如果定义这个方法该类的实例就可hash
class A:
def __init__(self):
self.a = ‘a’
self.b = ‘b’
def __hash__(self):
return 1
def __eq__(self, other):
return self.a == other.a
print(hash(A()))
print((A(),A()))
print({A(),A()})
s = {A(),A()}
print(s)
——————————-
1
(<__main__.A object at 0x000000872C048278>, <__main__.A object at 0x000000872C048390>)
{<__main__.A object at 0x000000872C048278>}
{<__main__.A object at 0x000000872C048278>}
other的解释:
class A:
def __init__(self):
self.a = ‘a’
self.b = ‘c’
def __hash__(self):
return 1
def __eq__(self, other):
# if not isinstance(other,A):
# return False
return self.a == other.a
class B:
def __init__(self):
self.a = ‘a’
self.b = ‘b’
def __eq__(self, other):
return self.b == other.b
class C:
c = ‘c’
a = A()
c = a
b = B()
print(a == b)#用a的eq方法与b的other.a比较由于都是“a”所以为ture
print(a is c)#
print(id(a), id(c))#id相同因为c对象为a的引用
print(b == C)#如果b在前则使用b的eq方法,由于C类没有定义b属性所以报错
———————-
Traceback (most recent call last):
True
File “F:/pycharm_product/python/1113/1.py”, line 28, in <module>
157269657528 157269657528
print(b == C)#如果b在前则使用b的eq方法,由于C类没有定义b属性所以报错
File “F:/pycharm_product/python/1113/1.py”, line 17, in __eq__
return self.b == other.b
AttributeError: type object ‘C’ has no attribute ‘b’
  • hash值相同不代表对象相同,比如hash算法为%3取模,此时3,6,9的hash值相同但是对象并不相同。
set去重会先判断is是否相同,如果不同再判断==是否相同,如都不同则会放到集合中
In [52]: b = (1,2,3)
In [53]: a = (1,2,3)
In [54]: id(b)
Out[54]: 917501314464
In [55]: id(a)
Out[55]: 917522737264
In [56]: c = {a,b}
In [57]: c
Out[57]: {(1, 2, 3)}#虽然内存地址不同但是内容相同还是做了去重处理
方法
意义
__eq__
对应==操作符,判断2个对象是否相等返回bool值
  • __hash__方法只是返回一个hash值作为set的key,但是去重,还需要__eq__来判断对象是否相等。
  • hash值相等,只是hash冲突,不能说明两个对象是相等的
  • 因此,一般来说hash方法是为了作为set或者dict的key,所以去重同时需要eq方法
  • 可hash对象昂必须提供__hash__方法,没有提供的话,instance(p1,collections.Hashable)一定为False。去重要提供__eq__方法。
  • 练习:
  • 设计二维坐标Point,比较2个坐标是否相等?
class Point:
def __init__(self,x,y):
self.x = x
self.y = y
def __eq__(self, other):
return self.x == other.x and self.y == other.y
a = Point(3,4)
c = Point(3,4)
d = Point(4,5)
print(a == c)
print(a == d)
print(a is c)
print(a is d)
———————-
True
False
False
False
  • 思考:为什么list不可以hash?
Alt text
* 通过查看原码发现__hash__ = None,也就是说如果调用__hash__()相当于调用None(),一定报错
* 所有的类都继承了object,而这个类是具有__hash__()方法的,如果一个类不能被hash,就把hash设置为None
bool
  • 四大皆空<list,tuple,set,dict>,__len__()为0所以必定为False
方法
意义
__bool__
内建函数bool(),或者对象放在逻辑表达式的位置,调用这个函数返回布尔值。没有定义__bool__(),就找__len__()返回长度,非0为真。如果__len__()也没有定义,那么所有实例都返回真
class A:
pass
print(bool(A()))
class B:
def __bool__(self):
return False
print(bool(B))
print(bool(B()))
class C:
def __len__(self):
return 0
print(bool(C()))
——————–
True
True
False
False
可视化
方法
意义
__repr__
内建函数repr()对一个对象获取字符串表达。如果一个类定义了__repr__()但没有定义__str__,那么在请求该类的‘非正式’的字符串表示时也将调用__repr__()
__str__
str()函数,内建函数format、print()函数调用,需要返回对象的字符串表达
__bytes__
bytes的时候,返回一个对象的bytes表达,即返回bytes对象
class A:
def __init__(self):
self.a = ‘a’
self.b = ‘b’
def __repr__(self):
return ‘repr:{},{}’.format(self.a,self.b)
def __str__(self):
return ‘str:{},{}’.format(self.a,self.b)
print(A())#print函数使用__str__
print([A()])#[]使用__str__,但其内部使用__repr__
print(([str(A())]))#[]使用__str__,str()函数也使用__str__
print(‘str:a,b’)
s = ‘b’
print([‘a’],(s,))
——————–
str:a,b
[repr:a,b]
[‘str:a,b’]
str:a,b
[‘a’] (‘b’,)
运算符重载
  • operator模块提供一下的特殊方法,可以将类的实例使用下面的操作符来操作
运算符
特殊方法
含义
<,<=,==,>,>=,!=
__lt__,__le__,__eq__,__gt__,__ge__,__ne__
比较运算符
+,-,*, /,%,//,**,divmod
__add__,__sub__,__mul__,__truediv__,__mod__,__floordiv__,__pow__,__divmod__
算数运算符,位运算符也有对应的方法
+=,-=,*=,/=,%=,//=,**=
__iadd__,__isub__,__imul__,__itruediv__,__imod__,__floordiv__,__ipow__
class A:
def __init__(self,x):
self.x = x
def __sub__(self, other):
return self.x – other.x
def __isub__(self, other):
tmp = self.x – other.x
return A(tmp)
def __str__(self):
return str(self.x)
x = A(5)
y = A(4)
print(x – y,x.__sub__(y))#直接使用减法跟调用sub方法效果一样,运算符重载
x -= y#实例化对象调用运算符
print(x)
—————————————–
1 1
1
  • 练习:完成Point类设计,实现判断点相等的方法,并完成向量的加法
class Point:
def __init__(self,x,y):
self.x = x
self.y = y
def __repr__(self):
return ‘self.x={},self.y={}’.format(self.x,self.y)
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __add__(self, other):
return self.x+other.x,self.y+other.y
def add(self,other):
return Point(self.x + other.x,other.y + self.y)
p1 = Point(1,1)
p2 = Point(1,1)
points = (p1,p2)
print(points[0].add(points[1]))
#运算符重载
print(points[0] + points[1])
#print(Point(*(points[0] + points[1])))提示*的选项不可迭代,原因是上面的__add__魔术方法中的return为Point(self.x+other.x,self.y+other.y)对象
print(p1 == p2)
运算符重载应用场景
  • 往往是用面向对象实现的类,需要做大量的运算,而运算符是这种在数学上最常见的表达方式。例如,上例中的对+进行了运算符重载,实现了Point类的二元操作,重新定义为Point+Point
  • 提供运算符重载,比直接提供加法方法要更加适合该领域内使用者的习惯
  • int类,几乎实现了所有操作符
容器的相关方法
方法
意义
__len__
内建函数len(),返回对象的长度(>0的整数),其实即使把对象当做容器类型看,就如同list或者dict。bool()函数调用的时候,如果没有__bool__()方法,则会看__len__()方法是否存在,存在返回非0为真
__iter__
迭代容器时,调用,返回一个新的迭代器对象
__contains__
in成员运算符,没有实现,就调用__iter__方法遍历
__getitem__
实现self[key]访问。序列对象,key接受整数为索引,或者切片。对于set和dict,key为hashable。key不存在引发keyError异常
__setitem__
和__getitem__的访问类似,是设置值得方法
__missing__
‘字典使用’__getitem__()调用时,key不存在执行该方法
*练习:将购物车改造成方便操作的容器类
class Cart:
def __init__(self):
self.items = []#定义购物车容器
def __len__(self):
return len(self.items)#返回对象长度
def additem(self,item):
self.items.append(item)#向购物车内添加商品
def __add__(self, other):#运算符重载,实例化后使用+即代表add
self.items.append(other)
return self
def __getitem__(self, index):#实例化对象通过索引获取value
return self.items[index]
def __setitem__(self,key,value):#实例化对象通过索引方式设置value
print(key,value)
self.items[key] = value
#self.[key] = value#不可以
def __iter__(self):#迭代和in注意要求返回必须是一个迭代器
return iter(self.items)#将列表容器转为装饰器对象,需要for或next获取内容
def __repr__(self):
return str(self.items)
cart = Cart()
cart.additem(1)
cart.additem(‘a’)
cart.additem(2)
print(len(cart))
print(‘~’*40)
for x in cart:
print(x)
print(‘~’*40)
print(2 in cart)#in操作,这个例子没有实现contains方法,就调用iter方法遍历
print(cart[1])#索引操作即调用getitem方法
cart[2] = ‘b’#调用setitem方法
print(cart[2])
print(cart + 2 + 3 + 4 + 5)#链式变成实现加法
cart.__add__(2).__add__(3)
print(‘~’*40)
for i in cart:
print(i)
print(cart.items)
———————–
3
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1
a
2
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
True
a
2 b
b
[1, ‘a’, ‘b’, 2, 3, 4, 5]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1
a
b
2
3
4
5
2
3
[1, ‘a’, ‘b’, 2, 3, 4, 5, 2, 3]
可调用对象
  • python中一切皆对象,函数也不例外
def foo():
print(foo.__module__,foo.__name__)
foo()#跟下面等价
foo.__call__()
———————-
__main__ foo
__main__ foo
  • 函数即对象,对象foo加上(),就是调用对象的__call__()方法
  • 可调用对象
方法
意义
__call__
类中的第一个该方法,实例就可以像函数一样调用
  • 可调用对象:定义一个类,并实例化得到其实例,将实例像函数一样调用。
class Point:
def __init__(self,x,y):
self.x = x
self.y = y
def __call__(self, *args, **kwargs):
return “Point({},{})”.format(self.x,self.y)
p = Point(4,5)
print(p)
print(p())
class Adder:
def __call__(self, *args):
ret = 0
for x in args:
ret += x
self.ret = ret
return ret
adder = Adder()
print(adder(4,5,6))
print(adder.ret)
————————
<__main__.Point object at 0x0000004F80B98780>
Point(4,5)
15
15
  • 练习:定义一个斐波那契数列的类,方便调用,计算第n项
#想法一,交换值
class Fib():
def __init__(self):
self.x = 0
self.y = 1
self.z = 0
self.fib = [0,1]
def __call__(self, *args, **kwargs):#由于args返回为一个tuple所以需要解构或者取第一个值
self.f = args[0]
for _ in range(2,self.f+1):
self.z = self.x + self.y
self.x = self.y
self.y = self.z
return self.z
fib = Fib()
print(fib(5))
—————-
5
#方法二,优势当传入60,再传入40时返回速度很快,因为第二个if将已存在的index直接取出
class Fib:
def __init__(self):
self.items = [0,1,1]
def __call__(self, index):
if index < 0:
raise IndexError(‘Wrong Index’)
if index < len(self.items):
return self.items[index]
for i in range(3,index+1):
self.items.append(self.items[i-1] + self.items[i-2])
return self.items[index]
print(Fib()(4))
#方法三:使用容器方法来实现,使用类实现斐波那契很好实现,还可以缓存数据,便于检索
class Fib:
def __init__(self):
self.items = [0,1,1]
def __call__(self, index):
return self[index]
def __iter__(self):
return iter(self.items)
def __len__(self):
return len(self.items)
def __getitem__(self, index):
if index < 0:
raise IndexError(‘Wrong Index’)
if index < len(self.items):
return self.items[index]
for i in range(len(self),index+1):
self.items.append(self.items[i-1] + self.items[i-2])
return self.items[index]
def __str__(self):
return str(self.items)
__repr__ = __str__
fib = Fib()
print(fib(5),len(fib))
print(fib(10),len(fib))
for x in fib:
print(x)
print(fib[5],fib[6])
上下文管理
  • 文件IO操作可以对文件对象使用上下文管理,使用with…as语法
with open(‘test’) as f:
pass
#仿照文件操作写一个类,实现上下文管理
class Point:
pass
with Point() as p:#AttributerError:__exit__,添加完exit后又报enter继续添加
pass
  • 上下文管理对象
  • 当一个对象同时实现了__enter__和__exit__()方法,它就属于上下文管理的对象
方法
意义
__enter__
进入与此对象相关的上下文,如果存在该方法,with语法会把该方法的返回值作为绑定到as子句中指定的变量上
__exit__
退出与此对象相关的上下文
#实例化对象上下文管理
class Point:
def __init__(self):
print(‘init’)
def __enter__(self):
print(‘enter’)
def __exit__(self, exc_type, exc_val, exc_tb):
print(‘exit’)
with Point() as f:
print(‘do sth.’)
  • 实例化对象的时候,并不会调用enter,进入with语句块调用__enter__方法,然后执行语句体,最后离开with语句块的时候,调用__exit__方法
  • with可以开启一个上下文运行环境,在执行前做一些准备,执行后做一些收尾工作
上下文管理的安全性
  • 看看异常对上下文的影响,通过下例可以看出在enter和exit照样执行,上下文管理是安全的
class Point:
def __init__(self):
print(‘init’)
def __enter__(self):
print(‘enter’)
def __exit__(self, exc_type, exc_val, exc_tb):
print(‘exit’)
with Point() as f:
raise Exception(‘error’)
print(‘do sth.’)
—————————–
Traceback (most recent call last):
init
File “F:/pycharm_product/python/1115/8.py”, line 12, in <module>
enter
raise Exception(‘error’)
exit
Exception: error
  • 极端例子:调用sys.exit(),它会退出当前解释器
  • 打开Python解释器,在里面敲sys.exit(),窗口直接关闭了,也就是说碰到这一句,Python运行环境直接退出了
import sys
class Point:
def __init__(self):
print(‘init’)
def __enter__(self):
print(‘enter’)
def __exit__(self, exc_type, exc_val, exc_tb):
print(‘exit’)
with Point() as f:
sys.exit()
print(‘do sth.’)
print(‘outer’)
———————–
init
enter
exit
  • 从执行结果来看,依然执行了__exit__函数,哪怕是退出Python运行环境
  • 由此说明上下文管理很安全
with语句
class Point:
def __init__(self):
print(‘init’)
def __enter__(self):
print(‘enter’)
def __exit__(self, exc_type, exc_val, exc_tb):
print(‘exit’)
p = Point()
with p as f:
print(p == f)#不相等
print(‘do sth.’)
———————————
init
enter
False
do sth.
exit
  • 不相等的原因在于__enter__方法上,它将自己的返回值赋给了f,继续修改测试看下例
import sys
class Point:
def __init__(self):
print(‘init’)
def __enter__(self):
print(‘enter’)
return self#加上此句则p与f相等
def __exit__(self, exc_type, exc_val, exc_tb):
print(‘exit’)
p = Point()
with p as f:
print(p == f)
print(p is f)
print(‘do sth.’)
——————
init
enter
True
True
do sth.
exit
  • __enter__方法返回值就是上下文使用的对象,with语法会把它的返回值赋给as子句的变量
  • __enter__方法和__exit__方法的参数
  • __enter__方法没有其他参数
  • __exit__方法有三个参数(self.exctype,excvalue,traceback)
  • 这三个参数都与异常有关
  • 如果该上下文退出时没有异常,这三个参数都是None
  • 如果有异常,参数意义如下:
    • exc_type:异常类型
    • exc_value:异常的值
    • traceback,异常的追踪信息
    • __exit方法返回一个等效True的值,则压制异常;否则继续抛出异常
import sys
class Point:
def __init__(self):
print(‘init’)
def __enter__(self):
print(‘enter’)
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print(exc_type)
print(exc_val)
print(exc_tb)
print(‘exit’)
return ‘abc’
p = Point()
with p as f:
raise Exception(‘New Error’)
print(‘do sth.’)
print(‘outer’)
——————————-
init
enter
<class ‘Exception’>
New Error#获取到的错误值
<traceback object at 0x000000517DA3BE88>
exit
outer
  • 练习:为加法函数计时
  • 1.使用装饰器显示该函数的执行时长
  • 2.使用上下文管理显示该函数的执行时长
#装饰器实现
import datetime
from functools import wraps
import time
def timeit(fn):
@wraps(fn)
def wrapper(*args,**kwargs):
start = datetime.datetime.now()
ret = fn(*args,**kwargs)
delta = (datetime.datetime.now() – start).total_seconds()
print(‘{} took {}s’.format(fn.__name__,delta))
return ret
return wrapper
@timeit
def add(x,y):
time.sleep(2)
return x+y
print(add(4,5))
#安全上下文实现:
import datetime
from functools import wraps
import time
def timeit(fn):
@wraps(fn)
def wrapper(*args,**kwargs):
start = datetime.datetime.now()
ret = fn(*args,**kwargs)
delta = (datetime.datetime.now() – start).total_seconds()
print(‘{} took {}s deco ‘.format(fn.__name__,delta))
return ret
return wrapper
@timeit
def add(x,y):
time.sleep(2)
return x+y
class TimeIt:
def __init__(self,fn):
self.fn = fn
def __enter__(self):
self.start = datetime.datetime.now()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.delta = (datetime.datetime.now() – self.start).total_seconds()
print(‘{} took {}s context’.format(self.fn.__name__,self.delta))
pass
def __call__(self, x,y):
print(x,y)
return self.fn(x,y)
# with TimeIt(add):
# add(4,5)
with TimeIt(add) as foo:
foo(4,5)
——————————————————————-
#把类当做装饰器用
import datetime
from functools import wraps
import time
class TimeIt:
def __init__(self,fn):
self.fn = fn
def __call__(self, *args,**kwargs):
self.start = datetime.datetime.now()
ret = self.fn(*args,**kwargs)
self.delta = (datetime.datetime.now() – self.start).total_seconds()
print(‘{} took {}s. call’.format(self.fn.__name__,self.delta))
return ret
@TimeIt
def add(x,y):
time.sleep(2)
return x+y
add(4,5)
print(add.__doc__)
——————————-
add took 2.000316s. call
None#无法获取被装饰函数的原属性,如文档字符串
———————————————————————-
##解决无法显示被装饰函数的字符串文档问题,原始方法可以将init中加入self.__doc__=fn.__doc__,不过这样繁琐且不全面
import datetime
from functools import wraps
import time
class TimeIt:
def __init__(self,fn):
self.fn = fn
wraps(fn)(self)#
def __call__(self, *args,**kwargs):
self.start = datetime.datetime.now()
ret = self.fn(*args,**kwargs)
self.delta = (datetime.datetime.now() – self.start).total_seconds()
print(‘{} took {}s. call’.format(self.fn.__name__,self.delta))
return ret
@TimeIt
def add(x,y):
“””This is add func”””
time.sleep(2)
return x+y
add(4,5)
print(add.__doc__)
上下文应用场景
1.增强功能:在代码执行的前后增加代码,以增强其功能,类似装饰器的功能
2.资源管理:打开了资源需要关闭,例如文件对象,网络连接,数据库连接等
3.权限验证:在执行代码前,做权限的验证,在__enter__中处理
contextlib.contextmanager
  • 它是一个装饰器实现上下文管理,装饰一个函数,而不用像类一样实现__enter__和__exit__方法
  • 对下面的函数有要求,必须有yield,也就是说这个函数必须返回一个生成器,且只有yield一个值
import contextlib
@contextlib.contextmanager
def foo():
print(‘enter’)
yield
print(‘exit’)
with foo() as f:
#raise Exception()只能打印enter无法执行yield和exit
print(f)
—————————-
enter
None
exit
  • f接收yield语句的返回值
  • 下面增加一个异常,发现不能保证exit执行,解决办法:增加try finally。
import contextlib
@contextlib.contextmanager
def foo():
print(‘enter’)
try:
yield#yield 5,yield的值只能有一个,作为__enter__方法的返回值
finally:
print(‘exit’)
with foo() as f:
raise Exception()
print(f)
————————-
Traceback (most recent call last):
enter
exit
File “F:/pycharm_product/python/1117/1.py”, line 12, in <module>
raise Exception()
Exception
  • 这样做得意义是,当yield发生处为生成器函数增加了上下文管理
import contextlib
import datetime
import time
@contextlib.contextmanager
def add(x,y):
start = datetime.datetime.now()
try:
yield x + y#yield 5,yield的值只能有一个,作为__enter__方法的返回值
finally:
delta = (datetime.datetime.now() – start).total_seconds()
print(delta)
with add(4,5) as f:
#raise Exception()
time.sleep(2)
print(f)
———————————–
9
2.000035
总结:如果业务逻辑简单可以使用函数加装饰器方式,如果业务复杂,用类的方式加__enter__和__exit__
反射
  • 概述:
    • 运行时,区别于编译时,指的是程序被加载到内容中执行的时候
    • 反射,reflection,指的是运行时获取类型定义信息
    • 一个对象能够在运行时,像照镜子一样,反射出其类型信息
    • 简单说,在python中,能够通过一个对象,找出其type、class、attribute或method的能力,成为反射或者自省
  • 反射相关函数和方法
  • 需求:有一个Point类,查看它实例的属性,并修改它,动态为实例加属性
class Point:
def __init__(self,x,y):
self.x = x
self.y = y
def __str__(self):
return “Point({},{})”.format(self.x,self.y)
def show(self):
print(self.x,self.y)
p = Point(4,5)
print(p)
print(p.__dict__)
p.__dict__[‘y’] = 6
print(p.__dict__)
p.z = 10
print(p.__dict__)
print(‘~’*40)
print(dir(p))
print(‘~’*40)
print(p.__dir__())
—————————————–
Point(4,5)
{‘x’: 4, ‘y’: 5}
{‘x’: 4, ‘y’: 6}
{‘z’: 10, ‘x’: 4, ‘y’: 6}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[‘__class__’, ‘__delattr__’, ‘__dict__’, ‘__dir__’, ‘__doc__’, ‘__eq__’, ‘__format__’, ‘__ge__’, ‘__getattribute__’, ‘__gt__’, ‘__hash__’, ‘__init__’, ‘__le__’, ‘__lt__’, ‘__module__’, ‘__ne__’, ‘__new__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__setattr__’, ‘__sizeof__’, ‘__str__’, ‘__subclasshook__’, ‘__weakref__’, ‘show’, ‘x’, ‘y’, ‘z’]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[‘z’, ‘__ge__’, ‘__ne__’, ‘__delattr__’, ‘__le__’, ‘x’, ‘__str__’, ‘__format__’, ‘__eq__’, ‘__init__’, ‘__weakref__’, ‘__class__’, ‘__dir__’, ‘__getattribute__’, ‘__reduce__’, ‘__new__’, ‘__dict__’, ‘__hash__’, ‘__subclasshook__’, ‘__reduce_ex__’, ‘__sizeof__’, ‘__setattr__’, ‘show’, ‘__repr__’, ‘y’, ‘__gt__’, ‘__doc__’, ‘__module__’, ‘__lt__’]
  • 上例通过属性字典__dict__来访问对象的属性,本质上也是利用的反射能力
  • python提供了内置的函数
内置函数
意义
getattr(object,name[,default])
通过name返回object的属性值,当属性不存在,将使用defalut返回,如果没有default,则抛出AttributeError。name必须为字符串
setattr(object,name,value)
object的属性存在,则覆盖,不存在,新增
hasattr(object,name)
判断对象是否有这个名字的属性,name必须为字符串
#测试getattr,setattr,hasattr
class Point:
def __init__(self,x,y):
self.x = x
self.y = y
def __str__(self):
return “Point({},{})”.format(self.x,self.y)
def show(self):
print(‘3’)
p1 = Point(4,5)
p2 = Point(10,10)
#getattr(Point,’show’)()#类无法调用自己的方法,可以使用类方法来增加类调用功能
print(repr(p1),repr(p2),sep=’\n’)
print(p1.__dict__)
setattr(p1,’y’,16)
setattr(p1,’z’,10)
print(getattr(p1,’__dict__’))
#动态调用方法如果show方法存在则调用
if hasattr(Point,’show’):
getattr(p1,’show’)()#getattr可以处理类或者实例化的对象
#动态增加方法
#为类增加方法
if not hasattr(Point,’add’):
setattr(Point,’add’,lambda self,other:Point(self.x+other.x,self.y+other.y))
print(Point.__dict__)
print(‘~’*40)
print(Point.add)
print(p1.add)
print(p1.add(p2))#绑定
print(‘~’*40)
#为实例增加方法,未绑定
if not hasattr(p1,’sub’):
setattr(p1,’sub’,lambda self,other:Point(self.x-other.x,self.y-other.y))
print(p1.sub(p1,p1))
print(p1.sub)
#add在谁里面,sub在谁里面
print(p1.__dict__)
print(Point.__dict__)
————————————–
<__main__.Point object at 0x000000B0E7EA89B0>
<__main__.Point object at 0x000000B0E7EA89E8>
{‘x’: 4, ‘y’: 5}
{‘x’: 4, ‘y’: 16, ‘z’: 10}
3
{‘__init__’: <function Point.__init__ at 0x000000B0E7E9B7B8>, ‘show’: <function Point.show at 0x000000B0E7E9B8C8>, ‘__dict__’: <attribute ‘__dict__’ of ‘Point’ objects>, ‘__weakref__’: <attribute ‘__weakref__’ of ‘Point’ objects>, ‘__module__’: ‘__main__’, ‘__str__’: <function Point.__str__ at 0x000000B0E7E9B840>, ‘__doc__’: None, ‘add’: <function <lambda> at 0x000000B0E7E9B730>}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<function <lambda> at 0x000000B0E7E9B730>
<bound method <lambda> of <__main__.Point object at 0x000000B0E7EA89B0>>
Point(14,26)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Point(0,0)
<function <lambda> at 0x000000B0E7E9B950>
{‘sub’: <function <lambda> at 0x000000B0E7E9B950>, ‘y’: 16, ‘z’: 10, ‘x’: 4}
{‘__init__’: <function Point.__init__ at 0x000000B0E7E9B7B8>, ‘show’: <function Point.show at 0x000000B0E7E9B8C8>, ‘__dict__’: <attribute ‘__dict__’ of ‘Point’ objects>, ‘__weakref__’: <attribute ‘__weakref__’ of ‘Point’ objects>, ‘__module__’: ‘__main__’, ‘__str__’: <function Point.__str__ at 0x000000B0E7E9B840>, ‘__doc__’: None, ‘add’: <function <lambda> at 0x000000B0E7E9B730>}
上例中的绑定和未绑定?
  • 思考:这种动态增加属性的方式和装饰器修饰一个类、Mixin方式的差异?
  • 这种动态增删属性的方式是运行时改变类或者实例的方式,但是装饰器或者Mixin都是定义时就决定了,因此反射能力具有更大的灵活性
  • 练习
  • 1.命令分发器,通过名称找对应的函数执行。思路:名称找对象的方法
#方法一:函数实现分发器
def dispatcher():
cmds = {}
def reg(cmd,fn):
if isinstance(cmd,str):
cmds[cmd] = fn
else:
print(‘error’)
#print(cmds)#查看字典中的内容,cmd:lambda内存地址
def run():
while True:
cmd = input(“plz input command:”)
if cmd.strip() == ‘quit’:
return
cmds.get(cmd.strip(),defaultfn)()#get得到一个函数方法所以后面需要加()来调用,如果get不存在的key则返回默认的defaultfn函数。
def defaultfn():
pass
return reg,run
reg,run = dispatcher()
reg(‘cmd1’,lambda :1)
reg(‘cmd2’,lambda :2)
run()
#方法二:用类方法实现分发器
#类实现分发器:
#1.注册方法
#2.执行方法
class Dispatcher():
def __init__(self):
self._run()#不用实例化也能用?,其实在类的调用中已经实例化,只不过没将实例化的对象赋给一个名字
def cmd1(self):
print(“I’m cmd1”)
def cmd2(self):
print(“I’m cmd2”)
def _run(self):
while True:
cmd = input(“please input key”)
if cmd.strip() == “quit”:
break
getattr(self,cmd,lambda : print(‘Unknown Command {}’.format(cmd)))()
Dispatcher()
#print(Dispatcher.__dict__)
  • 方法二中,使用getattr方法找到对象的属性的方式,比自己维护一个字典来建立名称和函数之间的关系好得多
反射相关的魔术方法
  • __getattr__()、__setattr__()、__delattr__(),三个魔术方法
  • __getattr__()测试:
class Base:
n = 0
class Point(Base):
z = 6
def __init__(self,x,y):
self.x = x
self.y = y
def show(self):
print(self.x,self.y)
def __getattr__(self, item):
return “missing {}”.format(item)
p1 = Point(4,5)
print(p1.x)
print(p1.z)
print(p1.n)
print(p1.t)
————————-
4
6
0
missing t
一个类的属性会按照继承关系找,如果找不到,就会执行getattr方法,如果没有这个方法,就会抛出AttributeError异常表示找不到属性
查找属性顺序为:
instance.dict—>instance.class.dict—>继承的祖先类(直到object)的dict—>调用getattr()
  • __setattr__()举例:
class Base:
n = 0
class Point(Base):
z = 6
def __init__(self,x,y):
self.x = x
self.y = y
def show(self):
print(self.x,self.y)
def __getattr__(self, item):
return “missing {}”.format(item)
def __setattr__(self, key, value):
print(“setattr {} ={}”.format(key,value))
p1 = Point(4,5)
print(p1.x)
print(p1.z)
print(p1.n)
print(p1.t)
p1.x = 50
print(p1.__dict__)
p1.__dict__[‘x’] = 60
print(p1.__dict__)
print(p1.x)
—————————
setattr x =4
setattr y =5
missing x
6
0
missing t
setattr x =50
{}#因为setattr魔术方法对拦截属性的添加,修改操作所以x,y都没在字典中
{‘x’: 60}
60
实例通过点设置属性,如同self.x = x,就会调用__setattr__(),属性要加到实例的dict中,就需要自己完成
setattr方法,可以拦截实例属性的添加、修改操作,如果要设置生效,需要自己操作实例的dict,此例子中需要再setattr函数中加入self.__dict__[key] = value
  • __delattr__()举例:
class Base:
n = 0
class Point(Base):
Z = 6
def __init__(self,x,y):
self.x = x
self.y = y
def __delattr__(self, item):
print(‘Can not del {}’.format(item))
p = Point(14,5)
del p.x
p.z = 15
del p.z
del p.Z
print(Point.__dict__)
print(p.__dict__)
del Point.Z
print(Point.__dict__)
——————————
Can not del x
Can not del z
Can not del Z
{‘__doc__’: None, ‘__delattr__’: <function Point.__delattr__ at 0x0000002959D3B7B8>, ‘Z’: 6, ‘__module__’: ‘__main__’, ‘__init__’: <function Point.__init__ at 0x0000002959D3B730>}
{‘x’: 14, ‘y’: 5, ‘z’: 15}
{‘__doc__’: None, ‘__delattr__’: <function Point.__delattr__ at 0x0000002959D3B7B8>, ‘__module__’: ‘__main__’, ‘__init__’: <function Point.__init__ at 0x0000002959D3B730>}
可以阻止通过实例删除属性的操作。但是通过类依然可以删除属性。如同Z
  • __getattribute__举例:
class Base:
n = 0
class Point(Base):
z = 6
def __init__(self,x,y):
self.x = x
self.y = y
def __getattr__(self, item):
return “missing {}”.format(item)
def __getattribute__(self, item):
return item
p1 = Point(4,5)
print(p1.__dict__)
print(p1.x)
print(p1.z)
print(p1.n)
print(p1.t)
print(Point.__dict__)
print(Point.z)
————————————
__dict__
x
z
n
t
{‘__init__’: <function Point.__init__ at 0x0000000100FCB730>, ‘__doc__’: None, ‘z’: 6, ‘__module__’: ‘__main__’, ‘__getattribute__’: <function Point.__getattribute__ at 0x0000000100FCB840>, ‘__getattr__’: <function Point.__getattr__ at 0x0000000100FCB7B8>}
6
  • 实例的所有的属性访问,第一个都会调用getattribute方法,它阻止了属性的查找,该方法应该返回(计算后的)值或者抛出一个AttributeError异常
  • 他的return值将作为属性查找的结果,如果抛出AttributError异常,则会直接调用getattr方法,因为表示属性没有找到
class Base:
n = 0
class Point(Base):
z = 6
def __init__(self,x,y):
self.x = x
self.y = y
def __getattr__(self, item):
return “missing {}”.format(item)
def __getattribute__(self, item):
# raise AttributeError(‘Not Found’)
# pass
# return self.__dict__[item]
return object.__getattribute__(self,item)
p1 = Point(4,5)
print(p1.__dict__)
print(p1.x)
print(p1.z)
print(p1.n)
print(p1.t)
print(Point.__dict__)
print(Point.z)
————————————-
{‘y’: 5, ‘x’: 4}
4
6
0
missing t
{‘__getattribute__’: <function Point.__getattribute__ at 0x00000068B1D3B840>, ‘__doc__’: None, ‘__init__’: <function Point.__init__ at 0x00000068B1D3B730>, ‘z’: 6, ‘__getattr__’: <function Point.__getattr__ at 0x00000068B1D3B7B8>, ‘__module__’: ‘__main__’}
6
  • getattribute方法中为了避免在该方法中无限递归,他的实现应该永远调用基类的同名方法以访问需要的任何属性,例如object.getattribute(self,name).
  • 注意:除非你明确的知道getattribute方法用来做什么,否则不要使用它
总结:
魔术方法
意义
__getattr__()
通过搜索实例、实例的类及祖先类查不到属性,就会调用此方法
__setattr__()
通过.访问实例属性,进行增加、修改都要调用它
__delattr__()
当通过实例来删除属性时调用此方法
__getattribute__()
实例所有的属性调用都从这个方法开始
  • 属性查找顺序
  • 实例调用__getattribute__()–>instance.__dict__–>instance.__class__.__dict__–>继承祖先类(直到object)的__dict__–>调用__getattr__()

本文来自投稿,不代表Linux运维部落立场,如若转载,请注明出处:http://www.178linux.com/88799

(0)
上一篇 2017-11-21 16:35
下一篇 2017-11-21 20:32

相关推荐

  • N25_第三周

    1、列出当前系统上所有已经登录的用户的用户名,注意:同一个用户登录多次,则只显示一次即可。 who | cut -d' ' -f1 | sort -u [root@server1 ~]# who  | cut -d' ' -f1 …

    Linux干货 2016-12-17
  • 【原创】Tengine+LUA+Nginx-GridFS+jemalloc编译安装

    操作系统 ubuntu 14.04 LTS Nginx-GridFS为nginx直接访问mongoDB的扩展模块,目前只支持到mongodb2.6.12 mongodb安装请自己百度 #安装依赖包保平安 apt-get install root-system-bin autoconf automake gcc c++ libgeoip-devlibxml++…

    Linux干货 2016-11-30
  • Linux的硬链接与软链接

    Linux 的硬链接与软链接 文件由文件名与数据组成,这在 Linux 上被分成两个部分:用户数据 (user data) 与元数据 (metadata)。用户数据,即文件数据块 (data block),数据块是记录文件真实内容的地方;而元数据则是文件的附加属性,如文件大小、创建时间、所有者等信息。在 Linux 中,元数据中的 inode 号(inode…

    Linux干货 2016-10-25
  • 文件通配符练习

    课后资料补充 * : 通配符,代表任意字符(0到多个)? : 通配符,代表一个字符# : 注释/ : 跳转符号,将特殊字符或通配符还原成一般符号| : 分隔两个管线命令的界定; : 连续性命令的界定~ : 用户的根目录$ : 变量前需要加的变量值! : 逻辑运算中的&q…

    Linux干货 2016-08-08
  • Linux无人值守安装

    前言     通常情况下,我们安装Linux操作系统的时候需要从光盘启动,通过系统光盘bootloader启动安装程序,进入交互式安装模式,由用户配置安装选项,如磁盘分区、选择安装包等操作;安装完毕后重启结束安装。在生产环境中,成百上千台服务器需要安装操作系统的时候如果也采用交互式安装模式,无疑是极其低效的。本文主要介…

    Linux干货 2015-05-18
  • 第五周练习

    1、显示当前系统上root、fedora或user1用户的默认shell; egrep “^(root|fedora|user1)” /etc/passwd | cut -d: -f7 2、找出/etc/rc.d/init.d/functions文件中某单词后边跟一组小括号的行,形如:hello(); grep “\<.*\>()” /etc/r…

    Linux干货 2017-08-04