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. Sublime text 3 中 Package Control安装

    安装前 ctrl+shift+p  在命令板中输入PC,如下图表示没安装: 使用ctrl+~调出sublime软件的控制台命令窗口:粘贴运行 import urllib.request,os,hash ...

  2. algorithm & bitwise operation & the best leetcode solutions

    algorithm & bitwise operation & the best leetcode solutions leetcode 136 single-number the b ...

  3. js & Input & Paste & Clipboard & upload & Image

    js & Input & Paste & Clipboard & upload & Image input paste upload image js Clip ...

  4. Vue SSR in Action

    Vue SSR in Action https://ssr.vuejs.org/ https://ssr.vuejs.org/api/ https://ssr.vuejs.org/guide/data ...

  5. 小程序 in action

    小程序 in action https://github.com/xgqfrms/xcx-taro taro https://taro-docs.jd.com/taro/docs/README.htm ...

  6. how to get window width in javascript

    how to get window width in javascript how to get window width in js How to Detect Screen Resolution ...

  7. nodejs 简单的模拟代理服务器

    https://nodejs.org/api/net.html#net_net_createconnection 代理前:client -> server 代理后:client -> pr ...

  8. react 异步的setState

    import React from "react"; class App extends React.Component { state = { a: 0 }; component ...

  9. 谁能成为数据储存领域领头羊?永久数据存储--NGK的终极使命!

    区块链的目的是永远存储交易网络的历史.NGK技术团队能够永久存储其去中心化账本的副本.这是其日后能进行审计关键.一些著名的团队,如Solana和SKALE,现在正在为此与NGK进行最后的集成,我们预计 ...

  10. 【从零开始撸一个App】Fragment和导航中的使用

    Fragment简介 Fragment自从Android 3.0引入开始,它所承担的角色就是显而易见的.它之于Activity就如html片段之于页面,好处无需赘述. Fragment的生命周期和Ac ...