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__().

Python中类的特殊属性和魔术方法的更多相关文章

  1. Python继承、多继承、魔术方法

    继承和多继承的概念和使用 super的用法 __str__ __repr__ __call__ 多继承方法解析顺序和Mix-in开发模式 魔术方法原理和作用 继承 定义类的时候,在类名后面的括号里填继 ...

  2. Python面向对象之私有属性和私有方法

    01. 应用场景及定义方式 应用场景 在实际开发中,对象 的 某些属性或方法 可能只希望 在对象的内部被使用,而 不希望在外部被访问到 私有属性 就是 对象 不希望公开的 属性 私有方法 就是 对象  ...

  3. Python 类的私有属性与私有方法

    1.隐藏的使用场景 在Python类中,有些属性和方法只希望在对象的内部被使用,而不希望在外部被访问到, 2.定义方式, 在属性名或方法名前增加两个下划线,定义的就是私有属性或方法 #其实这仅仅这是一 ...

  4. Python 基础之面向对象之常用魔术方法

    一.__init__魔术属性 触发时机:实例化对象,初始化的时候触发功能:为对象添加成员,用来做初始化的参数:参数不固定,至少一个self参数返回值:无 1.基本用法 #例:class MyClass ...

  5. Python中变量的属性以及判断方法

    1.变量的属性 在Python中,创建一个变量会给这个变量分配三种属性: id ,代表该变量在内存中的地址: type,代表该变量的类型: value,该变量的值: x = 10 print(id(x ...

  6. python 子类继承父类属性及实例化方法

  7. Python魔术方法-Magic Method

    介绍 在Python中,所有以"__"双下划线包起来的方法,都统称为"Magic Method",例如类的初始化方法 __init__ ,Python中所有的魔 ...

  8. Python 魔术方法指南

    入门 构造和初始化 构造定制类 用于比较的魔术方法 用于数值处理的魔术方法 表现你的类 控制属性访问 创建定制序列 反射 可以调用的对象 会话管理器 创建描述器对象 持久化对象 总结 附录 介绍 此教 ...

  9. python类:magic魔术方法

    http://blog.csdn.net/pipisorry/article/details/50708812 魔术方法是面向对象Python语言中的一切.它们是你可以自定义并添加"魔法&q ...

随机推荐

  1. Hacker101 CTF-Micro-CMS v2

    一.打开网站是这个样子 找到一个登录框,存在注入漏洞 3.我们可以这样更改用户名中的输入: admin' or 1=1 -- 4.错误消息显示Invalid Password,因此我们也应该尝试构造一 ...

  2. 使用nodejs爬取图片

    在运行代码前,请确保本机是否有nodejs环境 1 D:\ > node -v 2 v12.1.0 //版本号 需要用到的包 axios //请求页面 cheerio // 把get请求的页面 ...

  3. swiper & swiper slider

    swiper & swiper slider mobile swiper https://idangero.us/swiper/ https://idangero.us/swiper/get- ...

  4. window.ShadyCSS

    window.ShadyCSS Web Components # install $ yarn add @webcomponents/shadycss@1.7.1 # OR $ npm i @webc ...

  5. Suspense for Data Fetching

    Suspense for Data Fetching Experimental https://reactjs.org/docs/concurrent-mode-suspense.html React ...

  6. webIM & IM

    webIM & IM sdk 埋点 U-App AI https://developer.umeng.com/docs/67953/detail/68131 https://account.u ...

  7. Regular Expressions all in one

    Regular Expressions all in one Regular Expressions Cheatsheet https://developer.mozilla.org/en-US/do ...

  8. uniapp 万年历

    大量代码来至这里 <template> <view class="calendar-main"> <!-- 当前年月 --> <view ...

  9. c++ readIntger writeIntger

    类似CE的read/writeIntger函数(外部) #include <iostream> #include <Windows.h> #include <TlHelp ...

  10. NGK钱包真的安全吗?

    对于数字资产持有者而言,资产的安全永远是首要的,因而数字钱包的安全性显得尤为重要.数字钱包分为冷钱包和热钱包两种.热钱包叫做在线钱包,而冷钱包被称为离线钱包,也叫硬件钱包.数字钱包一旦被盗,被追回的概 ...