Python中类的特殊属性和魔术方法

1、属性

属性

含义

__name__

类、函数、方法等的名字   __dir__

__module__

类定义所在的模块名

__class__

对象或类所属的类   只是返回基类

__bases__

返回自己到object的类,类的基类元组,顺序为在基类列表中出现的顺序。

__doc__

类,函数的文档字符串,如果没有定义则为None。

__mro__

类的不是实例的。类的mro,class.mro()返回的结果保存在__mro__中。

__dict__

类或实例属性,可写的字典。

标识符和名称两码回事。。

2、查看属性

方法

意义

__dir__(只是影响实例)***

返回类或者对象的所有成员名称列表,dir()函数就是调用__dir__(),如果提供__dir__(),则返回属性的列表,否则会尽量从__dict__属性中收集信息。   收集很多信息

如果dir([object])参数obj包含方法__dir__(),该方法则被调用,如果参数obj中不包含__dir__(),该方法将最大限度的收集参数信息。

dir ()对不同类型的对象具有不同的行为。

如果对象是木块对象,返回的列表包含模块的属性名。

如果对象是类型或者类对象,返回的列表包含的属性名,以及他的基类的属性名。

否则,返回列表包含对象的属性名,他的类的属性名和类的基类的属性名。

 

import animal
from animal import Animal

class Cat(Animal):
    x = 'cat'
    y ='abcd'

class Dog(Animal):
    def __dir__(self):
        return ['dog']

print('-------')
print(1,'current modules \'s names = {}'.format(dir()))     #模块名词空间内的属性。
print(2,'anmial modules \'s names = {}'.format(dir(animal)))   #指定模块内的属性。
print(3,"object 's __dict__  = {}".format(sorted(object.__dict__.keys())))    #object 的字典。
print(4,"Animal's dir()={}".format(dir(Animal)))  #类Animal的dir()
print(5,"Cat's dir() = {}".format(dir(Cat)))    #类Cat的dir()
print('++++++++++++++++')
tom = Cat('tom')
print(6,sorted(dir(tom)))    #tom 的属性,cat类以及祖先类的属性。
print(7,sorted(tom.__dir__()))  #和上面一致,
print(8,sorted(set(tom.__dict__.keys())|set(Cat.__dict__.keys())|set(object.__dict__.keys())))

print(9,"Dog's dir = {}".format(dir(Dog)))
dog = Dog('snoppy')
print(10,dir(dog))
print(11,dog.__dict__)

 

animal Module's names = ['Animal', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']

-------

1 current modules 's names = ['Animal', 'Cat', 'Dog', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'animal']

2 anmial modules 's names = ['Animal', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']

3 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__']

4 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']

5 Cat'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', 'y']

++++++++++++++++

6 ['_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']

7 ['_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']

8 ['_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']

9 Dog'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']

10 ['dog']

11 {'name': 'snoppy', '_Animal__age': 10, 'weight': 20}

3、魔术方法***

分类:

    创建、初始化与销毁

__init__   初始化时候调用。(做资源申请,属性的建立等,存放数据等。)

     没有返回值。返回值是new函数做的。为实例编辑属性。属性在实例自己的dict的字典里面。

 

__del__   引用计数为0的时候。占用资源清理工作。不清理实例本身,只是清理连接等,(网络连接,直接联系的是打开和关闭资源等。)

 

 

hash: (__eq__方法等等,hash函数,把数据散列,把原数据散列,输入一定,输出也是一定的。)  幂等性。不过hash多少次,hash只是作为门牌号码,值怎么放hash不管。去重的话用到了__eq__函数。

 

bool : (做等效处理,如果没有__bool__方法,会找取__len__方法,)

 

可视化:print()  __repr__  __str__   __bytes__

Str调用内建函数str,print ,format会调用魔术方法str。

没有提供str会使用repr,没有提供repr的会采用祖先类的。

 

 

运算符重载:极其有作用。

 

容器大小:可迭代等由其完成。In操作。  geitem 和元素有关。

 

可调用对象: 

 

上下文管理:with as

 

反射:

 

描述器:

 

其他杂项:

 

4、hash   

在实例调用的时候起作用

__hash__

内建函数hash()调用的返回值,返回一个整数。如果定了这个方法该类的实例就可hash。

class A:
    def __init__(self,name,age=18):
        self.name =name

    def __hash__(self):
        return 1

    def __repr__(self):
        return self.name

print(hash(A('tom')))      #  1
print((A('tom'),A('tom')))   #(tom, tom)
print([A('tom'),A('tom')])    #[tom, tom]

s = {A('tom'),A('tom')}      #{tom, tom}
print(s)
print({tuple('t'),tuple('t')})    # {('t',)}
print({('tom,',),('tom',)})    #{('tom',), ('tom,',)}
print({'tom','tom'})    #{'tom'}

 

 

 

........
class A:
    def __init__(self,name,age=18):
        self.name =name

    def __hash__(self):
        return 1

    def __eq__(self, other):      等等函数作用
        return self.name == other.name

    def __repr__(self):
        return self.name

print(hash(A('tom')))      #  1
print((A('tom'),A('tom')))   #(tom, tom)
print([A('tom'),A('tom')])    #[tom, tom]

s = {A('tom'),A('tom')}      #{tom}
print(s)
print({tuple('t'),tuple('t')})    # {('t',)}
print({('tom',),('tom',)})    #{('tom',)}
print({'tom','tom'})    #{'tom'}

...................................................

什么是hash:解决能否hash的数据,通过hash函数散列成一个范围内的某个值。

散列。把散列值作为存储数据的位置。整数例外,特殊。 y=hash(x)

特点是:值发生一点变化,都会发生散列值的巨大的变化。

方法

意义

__eq__

对应==操作符,判断两个对象是够相等,返回bool值

 

__hash__ = None

不可hash 就是以上的设置。

__hash__方法只是返回一个hash值作为set的key,但是去重还是需要__eq__来判断两个对象是否相等。

 

hash值相等,只是hash冲突,不能说明两个值是相等的

因此,一般来说提供__hash__方法只是为了作为set或者dict的key,所以去重要求同时提供__eq__方法。

hash 只是解决了数据放在哪里的问题(数据存放问题)。不解决两个值一样的处理问题。(不不比较值相等的问题。)

(只是门牌号码,存放多少个值就不一样。)

 

不可hash对象isinstance(p1,collections.hashable)一定为False。

去重需要提供__eq__方法。

 

练习:设计二维坐标系,使其成为可hash类型,比比较两个坐标的实例是否相等。

class Point:
    def __init__(self,x,y):
        self.x = x
        self.y = y

    # def finish(self):
    #     return self.x,self.y
    #
    def __hash__(self):
        return  hash((self.x,self.y))

    def __eq__(self, other):
        return self.x == other.x and self.y == other.y

    def __repr__(self):
        return '{}{}'.format(self.x,self.y)
p1 = Point(4,5)
p2 = Point(4,5)

 

为什么list类实例不可以hash呢。

因为源码中有一句__hash__ =None ,所以如果调用了__hash__就相当于调用了None,一定报错,如果一个类不能hash就把__hash__设置为None。

.....................................................................................

5、bool

方法

意义

__bool__

内建函数bool(),或者对象放在逻辑表达式的位置,调用这个函数返回布尔值,没有定义__bool__(),就找__len__()返回长度,非0为真,如果 __len__()也没有定义,那么实例就为真。

 

 

class A:pass
print(bool(A()))
if A():
    print('real A')

class B:
    def __bool__(self):
        return False

print(bool(B))  
print(bool(B()))    False 因为实例化定义了为flase。

if B:
    print('real B')

class C:
    def __len__(self):
        return 0
print(bool(C))
if C:
    print('real c')

 

 

..................................................................................

可视化:

方法

意义

__repr__

内建函数repr()对一个对象获取字符串表达,调用__repr__返回字符串表达,如果__repr__也没有定义,就直接返回object的定义就显示内存地址信息。

__str__

Str()函数,内建函数format(),print()函数调用,需要返回对象的字符串表达,如果没有定义,就去调用__repr__方法返回字符串表达,如果__repr__没有定义,就直接返回对象的内存地址信息。

__bytes__

bytes()函数调用,返回一个对象的bytes表达,即返回bytes对象。

Print   format  str首先调用str方法。没有的话会找到__repr__。

 

class A:
    def __init__(self,name,age=1):
        self.name = name
        self.age = age

    def __repr__(self):
         return 'repr:{},{}'.format(self.name,self.age)

    def __str__(self):
         return 'str:{},{}'.format(self.name,self.age)

    def __bytes__(self):
         return '{}is{}'.format(self.name,self.age).encode()

print(1,A('tom'))    #1 str:tom,1
print(2,[A('tom')])   # 2 [repr:tom,1]
print(3,([str(A('tom'))]))    #3 ['str:tom,1']
print(4,bytes(A('tom')))       #4 b'tomis1'
print('str:a,1')                   

 

 

执行bytes 和list时候会报错,因为是在构建函数,后面的不是可迭代对象。

.....................................................................

6、运算符重载

Operator模块提供以下的特殊方法,可以将类的实例使用下面的操作符来操作。

运算符

特殊方法

含义

<,<=,==,>,>=,!=

__lt__,__le__,__eq__,__gt__,__ge__,__ne__

比较运算符

+,-,*,/,%,//,**,divmod

__add__,__sub__,__mul__,__truediv__,__mod__,__floordiv__,__pow__,__divmod__

算数运算符,移位,位运算也有对应的方法。

+=,-=,*=,/=,%=,//=,**=

__iadd__,__isub__,__imul__,__itruediv__,__imod__,__ifloordiv__,__ipow__

 

class A:
    def __init__(self,name,age=15):
        self.name = name
        self.age = age

    def __sub__(self, other):
        return self.age - other.age

    def __isub__(self, other):
        return A(self.name,self - other)

tom = A('tom')
jerry = A('jerry',16)

print(tom - jerry)   #-1
print(tom - jerry,jerry.__sub__(tom))   #-1   1

 

6.1练习,完成point类设计,实现判断点相等的方法,并完成向量的加法。

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

    def __add__(self, other):
        return Point(self.x+other.x,self.y+other.y)

    def __str__(self):
        return '{},{}'.format(self.x,self.y)

p1 = Point(1,1)
p2 = Point(2,2)
points = (p1,p2)
print(points[0]+points[1])

7、运算符重载应用场景

往往是面向对象实现的类,需要做大量的运算。Int类实现了所有操作符。

 

From functools import total_ordering

8、@functools.total_ordering   装饰器

__lt__, __le__ ,__eq__, __gt__ ,__ge__ 是比较大小必须实现的方法啊,利用@functools.total_ordering 装饰器就可以大大简化代码。

使用装饰器的时候__eq__必须实现其他方法,等等,大于小于等实现其一就可以。

 

from functools import total_ordering

@total_ordering
class Person:
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def __eq__(self, other):
        return self.age == other.age

    def __gt__(self, other):
        return self.age > other.age

tom = Person('tom',20)
jerry = Person('jerry',18)

print(tom>jerry)    #True
print(tom<jerry)   ##False
print(tom==jerry)  # False
print(tom>=jerry)   #True
print(tom<=jerry)   #False

 

装饰器虽然大大简化代码。但是会带来性能问题,所以需要什么方法自己去创建就可以了。

 

一共六种比较,只是需要创建三样就可以了。

 

class Person:
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def __eq__(self, other):
        return self.age == other.age

    def __gt__(self, other):
        return self.age > other.age

    def __ge__(self, other):
        return self.age >= other.age

tom = Person('tom',20)
jerry = Person('jerry',18)

print(tom>jerry)    #True
print(tom<jerry)   ##False
print(tom==jerry)  #False
print(tom>=jerry)   #True
print(tom<=jerry)   #False
print(tom!=jerry)   #True

 

 

 

9、容器相关方法

方法

意义

__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 A(dict):
    def __missing__(self, key):
        print('missing key:',key)
        return 0

a = A()
print(a['k'])      #missing key: k    0

9.1 为什么空字典,空字符串,空元组,空集合,空列表可以等效为False。

因为空的这些采用内建函数len,长度为0,所以等效为False。

9.2练习 :将购物车改造成方便操作的容器类

class Cart:
    def __init__(self):
        self.items = []

    def __len__(self):
        return len(self.items)

    def __iter__(self):
        return iter(self.items)

    def additem(self,item):
        self.items.append(item)

    def __add__(self, other):
        self.items.append(other)
        return self

    def __getitem__(self,index):
        return self.items[index]

    def __setitem__(self, key, value):
        self.items[key] = value

    def __str__(self):
        return str(self.items)

cart = Cart()
cart.additem(1)
cart.additem('abc')

print(len(cart))  #  2
print(bool(cart))   # True

for x in cart:
    print(x)   #   1   abc

print(3 in cart)   #  False

print(cart[1])    #    abc

print(cart + 4 + 5 + 6)   #  [1, 'abc', 4, 5, 6]
print(cart.__add__(12).__add__(13))    #   [1, 'abc', 4, 5, 6, 12, 13]

 

 

__getitem__  列表和字典都是通过key访问。

必须记住;

 

__missing__是和字典相关的。

 

.............................................完美分割线...................................................

10、可调用对象

__closure__闭包,

Callable可调用对象。

a()相当于是a.__call__()调用。

函数即对象,对象A加上()就是调用对象的__call__()方法。

方法

意义

__call__

类中定义一个该方法,实例就可以像函数一样调用

 


class Point:
    def __init__(self,x,y):
        self.x = x
        self.y = y

    def __call__(self, *args, **kwargs):
        return '{}:{}'.format(self.x,self.y)

p = Point(4,5)
print(p)   #  <__main__.Point object at 0x000000B420CB2898>
print(p())   #4:5

 

class Adder:
    def __call__(self, *args, **kwargs):
        ret = 0
        for x in args:
            ret += x
        self.ret = ret
        return ret

adder = Adder()
print(adder(4,5,6))   #15
print(adder.ret)   #15

10.1斐波那契数列。

class Fib:
    def __init__(self):
        self.items = [0,1,1]

    def __call__(self,n):
        l = len(self.items)
        if n <= 0:
            raise IndexError
        elif n < len(self.items):
            return self.items[n]

        for i in range(3,n+1):
            x = self.items[i-1] + self.items[i-2]
            self.items.append(x)
        return x

fib = Fib()
print(fib(10))

 

 

class Fib:

    def __init__(self):
        self.lst = [0,1,1]

    def __call__(self, index):
        return self[index]

    def __len__(self):
         return len(self.lst)

    def __iter__(self):
        return iter(self.lst)

    def __getitem__(self, index):
        if index <0:
            raise IndexError
        if index < len(self.lst):
            return self.lst[index]

        for i in range(len(self),index+1):
            self.lst.append(self.lst[i-1]+self.lst[i-2])
        return self.lst[index]

    def __str__(self):
        return str(self.lst)
   
fib = Fib()
print(fib(10))

 

 

 

 

 

 

斐波那契数列

 

 

 

 

........................................................完美分割线...................................................................

 

 

 

 

enter的返回值作用不影响。

 

11、上下文管理

文件IO操作可以对文件对象使用上下文管理,使用with..as语法。

With open(‘test’)as f:

Pass

 

class Point:
    pass

with Point() as p:
    pass

 提示错误信息,因为没有__exit__这个属性。

12、上下文管理对象

当一个对象同时实现了__enter__() 和__exit__()方法,他就属于上下文管理的对象。

方法

意义

__enter__

进入与此对象相关的上下文,如果存在此方法,with语法会把该方法的返回值作为绑定到as子句中指定的变量上

__exit__

退出与此对象相关的上下文。

 

import time
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 p:
    print('to do ')
    time.sleep(2)

 

实例化的时候,并不会调用enter,进入with语句块调用__enter__ 方法,然后执行语句体,最后离开with语句的时候会调用__exit__语句。

 

13、上下文管理的安全性

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 p:
    raise SyntaxError('error')
    print('to do ')

 

 

可以看出在进入和退出的时候照样执行函数,上下文管理是安全的。

极端的例子就是采用退出当前解释器的函数,sys.exit()窗口直接关闭了,Python运行环境推出了,但是enter 和exit函数照样执行。

14、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)

 

 

p和f是不一样的,因为p是实例对象,f却是enter的返回值。

__enter__方法返回值就是上下文中使用的对象,with语法会把其返回值赋值给as子句的变量。

 

With 可以开启一个上下文运行的环境,在执行前做一些准备工作,执行后做一些收尾工作。

 

__enter__  进入

__exit__   退出     碰到with的时候才会调用。

首先创建实例先调用__init__

 

With  A( )as f:

f的值是__enter__的返回值。

 

F = A()

With  f:

f就是实例,

 

15、__enter__ 方法和 __exit__方法的参数

__enter__ 的参数就是实例本身。

__exit__  的参数.一共是三个。如果退出时候没有异常,则这三个值 是None。

如果存在异常,参数意义如下:

exc_type ,异常类型。

exc_value,异常的值。

traceback 异常的追踪信息。

__exit__方法返回一个等效True的值,则会压制异常,否则,继续抛出异常。

 

class Point:
    def __init__(self):
        print('init')

    def __enter__(self):
        print('enter')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('exit')
        print(exc_type)   #<class 'SyntaxError'>
        print(exc_val)   #error
        print(exc_tb)    #<traceback object at 0x000000E91743AE88>
        return 'abc'
p = Point()
with p as f:
    raise SyntaxError('error')
    print('to do ')

15.1练习:

为加法函数计时。

第一种方法使用装饰器。

import datetime
import time


def timeit(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(3,4))

 

利用上下文实现:

import datetime
import time
from functools import wraps

def timeit(fn):
    """This is a 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):
    """this is a add func"""
   
time.sleep(2)
    return x + y

print(add(3,4))

class Timeit:
    def __init__(self,fn):
        self.fn = fn

    def __enter__(self):
        self.start = datetime.datetime.now()
        return self.fn

    def __exit__(self, exc_type, exc_val, exc_tb):
        delta = (datetime.datetime.now() - self.start).total_seconds()
        print('{}took {}s'.format(self.fn.__name__,delta))

with Timeit(add)as fn:
    print(add(4,7))

 

利用可调用对象来实现。

import datetime
import time
from functools import wraps

def timeit(fn):
    """This is a 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):
    """this is a add func"""
   
time.sleep(2)
    return x + y

print(add(3,4))

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):
        delta = (datetime.datetime.now() - self.start).total_seconds()
        print('{}took {}s'.format(self.fn.__name__,delta))

    def __call__(self, x, y):
        return self.fn(x,y)

with Timeit(add)as timeobject:
    print(timeobject(4,7))

 

把类当做装饰器来实现

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'.format(self.fn.__name__,self.delta))

    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 a add func"""
   
time.sleep(2)
    return x + y

add(3,4)

 

解决文档字符串的问题:   实例的__doc__ =函数的__doc__

class Timeit:
    def __init__(self,fn):
        self.fn = fn
        self.__doc__ = fn.__doc__

    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'.format(self.fn.__name__,self.delta))

    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 a add func"""
   
time.sleep(0.5)
    return x + y

add(3,4)
print(Timeit(add).__doc__)    #this is a add func
print(add.__doc__)            #this is a add func

利用funtools工具。

import time
import datetime
from functools import wraps,update_wrapper


class Timeit:
    """this a class"""
   
def __init__(self,fn):
        self.fn = fn
        # self.__doc__ = fn.__doc__     把函数对象的文档字符串直接赋给类
        # update_wrapper(self,fn)   
        wraps(fn)(self)

    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'.format(self.fn.__name__,self.delta))

    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 a add func"""
   
time.sleep(0.5)
    return x + y

add(3,4)
print(Timeit(add).__doc__)
print(add.__doc__)

类即可以用在上下文管理,又可以用作装饰器。

 

 

 

 

 

16、上下文管理用用场景

1、增强功能。

在代码执行的前后增加代码,以增强其功能,类似装饰器的功能,

2、资源管理。

打开资源需要关闭,例如文件对象,网络连接,数据库连接等。

3、权限验证。  在执行代码之前,做权限的验证, __enter__时候管理。

 

上下文不管异常有多强,清理等依然进行处理。

 

..............................................................分割线............................................................

17、Contextlib.contextmanager

他是一个装饰器实现上下文管理,装饰一个函数,而不用像类,一样实现__enter__和 __exit__方法。

对下面的函数有要求,必须有yield,也就是这个函数必须返回一个生成器,且只有yield一个值,也就是这个装饰器接受一个生产期函数作为参数。

@contextlib.contextmanager
def foo():
    print('enter')   #相当于__enter__()
    yield 100      #yield值只能有一个,作为__enter__的返回值,
    print('exit')   #相当于__exit__()

with foo() as f:
    print(f)

 

as 后面的变量接的是yield语句返回的值。

@contextlib.contextmanager
def foo():
    print('enter')   #相当于__enter__()
    try:
        yield 100      #yield值只能有一个,作为__enter__的返回值,
    finally:
        print('exit')   #相当于__exit__()

with foo() as f:
    raise IndexError
    print(f)

 

遇到异常依然会执行相应的语句。

import contextlib
import datetime
import time


@contextlib.contextmanager
def add(x,y):
    start = datetime.datetime.now()
    try:
        yield x+y
    finally:
        delta = (datetime.datetime.now()-start).total_seconds()
        print(delta)

with add(3,4) as f:
    time.sleep(1)
    print(f)

 

总结,如果业务逻辑简单可以使用函数加contextlib.contextmanager装饰器方式,如果业务复杂,用类的方式加__enter__  和__exit__ 方法方便。

 

..............................................................分割线............................................................

 

18、反射

概述:

运行时候(runtime),区别于编译时,指的是程序被加载到内存中执行的时候。

反射,reflection指的是运行时获取类型定义信息。

一个对象能够在运行时候,像照镜子一样,反射出其类型信息。

在Python中,能够通过一个对象,找出其type,class,attribute或method的能力,成为反射或者自省。

具有反射能力的函数有:type(),isintance(),callable(),dir(),getattr()。

19、反射相关的函数和方法

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)    # Point(4,5)
print(p.__dict__)   #   {'x': 4, 'y': 5}
p.__dict__['y']=16   
print(p.__dict__)     #{'x': 4, 'y': 16}
p.z = 10 
print(p.__dict__)     #{'z': 10, 'x': 4, 'y': 16}
print(dir(p))       #
print(p.__dir__())    #

 

Point(4,5)

{'x': 4, 'y': 5}

{'x': 4, 'y': 16}

{'z': 10, 'x': 4, 'y': 16}

['__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']

['__repr__', '__le__', '__str__', '__ge__', '__eq__', '__init__', 'z', '__hash__', '__lt__', '__subclasshook__', '__reduce_ex__', 'y', '__new__', '__ne__', '__dict__', '__sizeof__', '__setattr__', 'show', '__reduce__', '__weakref__', '__delattr__', '__format__', '__getattribute__', '__doc__', '__class__', '__gt__', '__dir__', '__module__', 'x']

 

通过属性字典__dict__来访问对象的属性,本质上也是利用反射能力。

Python提供了内建函数,来访问属性。

内建函数

意义

getattr(object,name[,default])

通过name返回object的属性值,当属性不存在,将使用default,如果没有default,则抛出属性异常,name必须为字符串

setattr(object,name,value)

Object的属性存在,则覆盖,不存在,新增。

hasattr(object,name)

判断对象是否有这个名字的属性,name必须有字符串

 

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)

p1 = Point(4,5)
p2 = Point(9,10)
print(repr(p1),repr(p2),sep = '\n')  #<__main__.Point object at 0x000000908D129550>
print(p1.__dict__)   #<__main__.Point object at 0x000000908D129588>
setattr(p1,'y',2)
setattr(p1,'z',3)    #{'y': 5, 'x': 4}
print(getattr(p1,'__dict__'))   #{'z': 3, 'y': 2, 'x': 4}

#动态调用
if hasattr(p1,'show'):
    getattr(p1,'show')()      #Point(4,2)

##动态增加方法
if not hasattr(Point,'add'):
    setattr(Point,'add',lambda self,other:Point(self.x+other.x,self.y+other.y))

print(Point.add)   #<function <lambda> at 0x000000D7350E4C80>
print(p1.add)   #<bound method <lambda> of <__main__.Point object at 0x0000007FA5749550>>
print(p1.add(p2))  #Point(13,12)

if not hasattr(p1,'sub'):
    setattr(p1,'sub',lambda self,other:Point(self.x-other.x,self.y-other.y))

print(1,p1.sub(p1,p1))  #Point(0,0)
print(2,p1.sub)     #  2 <function <lambda> at 0x000000304868B1E0>

print(p1.__dict__)   #{'z': 3, 'y': 2, 'sub': <function <lambda> at 0x0000000CDAD0B1E0>, 'x': 4}
print(Point.__dict__)
{'show': <function Point.show at 0x0000000CDAD0B158>, '__init__': <function Point.__init__ at 0x0000000CDAD04BF8>, '__module__': '__main__', 'add': <function <lambda> at 0x0000000CDAD04D08>, '__doc__': None, '__str__': <function Point.__str__ at 0x0000000CDAD0B0D0>, '__dict__': <attribute '__dict__' of 'Point' objects>, '__weakref__': <attribute '__weakref__' of 'Point' objects>}
 

 

 

动态增删属性的方式就是运行时改变类或者实例的方式,但是装饰器或Mixin都是定义时候就决定了。

因此反射能力具有更大的灵活性。

 

19.1利用属性方法创建命令分发器。

class Dispatcher:
    def __init__(self):
        self.run()

    def cmd1(self):
        print('cmd1')

    def  cmd2(self):
        print('cmd2')

    def run(self):
        while True:
            cmd = input('>>>').strip()
            if cmd == 'quit':
                break
            getattr(self,cmd,lambda :print('unknown command'.format(cmd)))()
Dispatcher()

 

 

 

 

 

..............................................................分割线..................................................................................

20、反射相关的魔术方法

方法

意义

__getattr__

针对已经有的属性无效,针对没有的属性。只是和实例有关。

当搜索实例,实例的类即祖先类查不到属性,就会调用此方法

__setattr__

通过实例属性,进行增加,修改(覆盖)都要调用它

 

__delattr__

当通过实例来删除属性时调用此方法

__getattrbute__

实例所有的属性调用都从这个方法开始。

属性查找顺序:

实例调用__getattribute__() 到对象的字典,对象的类的字典,继承祖先类的字典,调用__getattr__()

***第一个路,按照其返回值。

***第二条路,所有字典都去查找,没有的话,会调用getattr方法。

1)__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)   #4
print(p1.y)   #5
print(p1.z)   #6
print(p1.n)    #0
print(p1.t)    #missing t
print(p1.d)    #missing d
 

 

一个类的属性会按照继承关系找,如果找不到,就会执行__getattr__()方法,如果没有这个方法,就会抛出属性异常。

 

查找属性的顺序为:

对象的字典,对象自己的类的字典,祖先的字典,直到object的字典里面。找不到的话调用

 

2)__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)  # 4
print(p1.y)  # 5
print(p1.z)  # 6
print(p1.n)  # 0
print(p1.t)  # missing t
print(p1.d)  # missing d
p1.x = 50
print(1,p1.__dict__)   #1 {}
p1.__dict__['x'] = 60
print(p1.__dict__)  #{'x': 60}
print(p1.x)   #60

 

实例通过点设置属性,如同self.x = x,就会调用__setattr__(),属性要加到实例的__dict__中,就需要自己去完成。

 

3)__delattr__()

 

class Point:
    z = 5
    def __init__(self,x,y):
        self.x = x
        self.y = y

    def __delattr__(self, item):
        print('can not del{}'.format(item))

p = Point(2,3)
del p.x
p.z = 15
del p.z
del p.z
print(Point.__dict__) 
print(p.__dict__)   #{'z': 15, 'x': 2, 'y': 3}
del Point.z
print(Point.__dict__)

 

can not delx

can not delz

can not delz

{'z': 5, '__init__': <function Point.__init__ at 0x000000A16F054D08>, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'Point' objects>, '__delattr__': <function Point.__delattr__ at 0x000000A16F054BF8>, '__doc__': None, '__weakref__': <attribute '__weakref__' of 'Point' objects>}

{'z': 15, 'x': 2, 'y': 3}

{'__init__': <function Point.__init__ at 0x000000A16F054D08>, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'Point' objects>, '__delattr__': <function Point.__delattr__ at 0x000000A16F054BF8>, '__doc__': None, '__weakref__': <attribute '__weakref__' of 'Point' objects>}

 

可以组织实例删除属性的操作,但是通过类依然可以删除属性。

 

4)__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(2,3)
print(p1.__dict__)   #__dict__
print(p1.x)   #x
print(p1.z)   #z
print(p1.y)   #y
print(p1.n)   #n
print(p1.t)   #t
print(Point.__dict__)   #
print(Point.z)   #6

 

实例的所有的属性访问,第一个都会被调用__getattrbute__方法,阻止了属性的查找,该方法应该返回计算后的值或者抛出属性异常。

他的return值作为属性查找的结果,如果抛出属性异常,就会调用__getattr__方法,因为表示属性没有找到。(就是通过实例.的访问返回值。)

 

总结:

__getattrbute__  方法为了避免在该方法中无限的递归,他的实现应该永远调用基类的同名方法用来访问需要的任何属性,例如:object.__getattrbute__(self,name)

 

一般不建议使用。

属性查找顺序:实例调用__getattrbute__ () ---instance的字典--instance 的类的字典--继承到祖先类的字典(直到object)然后调用__getattr__().      

posted @ 2018-11-12 23:17  Python爱好者666  阅读(940)  评论(0编辑  收藏  举报