Python进阶10---魔术方法*

特殊属性

查看属性

 

#animal.py
class Animal:
    x = 123
    def __init__(self,name):
        self._name = name
        self.__age = 10
        self.weight = 20

print('animal Module\'s names = {}'.format(dir()))
#animal Module's names = ['Animal', '__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']

#cat.py
import animal
from animal import Animal
class Cat(Animal):
    x = 'cat'
    y = 'abcd'

class Dog(Animal):
    def __dir__(self):
        return ['dog']#指定返回列表

print('*'*10)
print('Current Module : {}'.format(dir()))
print('animal Module : {}'.format(dir(animal)))
print('Cat Module : {}'.format(dir(Cat)))
print('object Module : {}'.format(sorted(object.__dict__)))
print('object Module : {}'.format(sorted(object.__dict__.keys())))
print('*'*10)
tom = Cat('tom')
dog = Dog('dog')
print(sorted(dir(tom)))
print(sorted(tom.__dict__))
print(sorted(dir(Dog)))
print(sorted(dir(dog)))
#dir()的等价,近似如下,__dict__字典中几乎包括了所有属性
print(sorted(set(tom.__dict__.keys())|set(Cat.__dict__.keys())|set(object.__dict__.keys())))

魔术方法

hash

class A:
    def __init__(self):
        self.a = 'a'
        self.b = 'b'

    def __hash__(self):
        return 1

print(hash(A()))#1
print(A(),A())#<__main__.A object at 0x00000000028F54E0> <__main__.A object at 0x00000000028F55F8>
a = A()
print({A(),A()})#{<__main__.A object at 0x00000000028F55F8>, <__main__.A object at 0x0000000002993F60>}
s = {a,a}
print(s)#{<__main__.A object at 0x00000000028F54E0>} 
# set内部机制会先调用is判断是否是同一对象的引用
#如果想要实现set剔除相同的key,还需要增加__eq__函数
class A:
    def __init__(self):
        self.a = 'a'
        self.b = 'b'

    def __hash__(self):
        return 1

    def __eq__(self, other):
        return self.a == other.a    #相当于是return True

print(hash(A()))#1
print(A(),A())#<__main__.A object at 0x00000000029054E0> <__main__.A object at 0x00000000029055F8>
print({A(),A()})#{<__main__.A object at 0x00000000029055F8>}
s = {A(),A()}
print(s)#{<__main__.A object at 0x00000000029055F8>}

 

查看源码后发现源码中有一句__hash__=None,也就是如果调用__hash__()相当于None(),一定报错。所有类都继承object,
而这个类是具有__hash__()方法的,如果一个类不能被hash,就是把__hash__设置成None了。
#注意
from collections import Hashable
print(isinstance(list(),Hashable))#False
print(isinstance(list,Hashable))#True
class Point:
    def __init__(self,x,y):
        self.x = x
        self.y = y

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

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

p1 = Point(4,5)
p2 = Point(4,5)
print(hash(p1))#3713084879518070856
print(hash(p2))#3713084879518070856
print(p1 is p2)#False
print(p1 == p2)#True
print(set((p1,p2)))#{<__main__.Point object at 0x0000000002993E48>}
print(isinstance(p1,Hashable))#True

bool

class A:
    pass

print(bool(A))#True
print(bool(A()))#True

class B(object):
    def __bool__(self):
        return False

print(bool(B))#True
print(bool(B()))#False

class C:
    def __len__(self):
        return 0

print(bool(C))#True
print(bool(C()))#False 

可视化

 

class A:
    def __init__(self):
        self.a = 'a'
        self.b = 'b'

    def __repr__(self):
        return 'repr:{} {}'.format(self.a,self.b)

    def __str__(self):
        return 'str:{} {}'.format(self.a,self.b)

print(A())#print函数使用__str__
print([A()])#[]使用__str__,但其内部使用__repr__
print(str(A()))#[]使用__str__,str()函数也使用__str__
a1 = A()
a2 = A()
lst = [a1,a2]
print(lst)#[repr:a b, repr:a b]
for i in lst:  #str:a b    str:a b
    print(i)  

运算符重载

 

class A:
    def __init__(self,x):
        self.x = x

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

    def __isub__(self, other):
        tmp = self.x - other.x
        return A(tmp)

    def __lt__(self, other):
        return self.x < other.x

    def __repr__(self):
        return str(self.x)

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


a = A(1)
b = A(3)
c = A(5)
lst = [a,c,b]
print(sorted(lst))#[1, 3, 5]
x = A(5)
y = A(4)
print(x-y,x.__sub__(y))#1  1
x -= y
print(x)#1 

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

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

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

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

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

p1 = Point(1,1)
p2 = Point(1,1)
points = (p1,p2)
print(points[0].add(points[1]))#Point:2,2
#运算符重载
print(points[0]+points[1])#(2, 2)
print(Point(*(points[0]+points[1])))#Point:2,2
print(p1 == p2)#True

运算符重载应用场景

容器相关方法

 

#购物车
class Item:
    def __init__(self,name,**kwargs):
        self.name = name
        self._spec = kwargs

    def __repr__(self):
        return "{} = {}".format(self.name,self._spec)

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

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

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

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

    def __iter__(self):#迭代和in
        return iter(self.items)#未加iter()则会TypeError: iter() returned non-iterator of type 'list'

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

    def __getitem__(self, index):#索引操作
        return self.items[index]

    def __setitem__(self, key, value):#索引赋值运算
        # print(key,value)
        self.items[key] = value
        # self[key] = value #不可以,这种方式相当于调用上面的__getitem__

    # def __missing__(self, key):#针对dict/set key missing有效
    #     print('key=',key)

cart = Cart()
print(len(cart))
print(cart+2+3)
#__iter__
for i in cart:
    print(i)
print('*'*10)
print(cart[1])

cart[1] =100
#链式编程实现加法
print(cart+2+3+4)
cart.__add__(2).__add__(3)

可调用对象

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

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

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

    def __call__(self,index):
        if index<0:
            return IndexError('Wrong Index')
        if index < len(self.lst):
            return self.lst[index]
        for i in range(len(self.lst),index+1):
            self.lst.append(self.lst[i-2] + self.lst[i-1])
        return self.lst[index]

    def __getitem__(self, index):
        return self.lst[index]
    def __str__(self):
        return str(self.lst)

    __repr__ =  __str__
fib = Fib()
print(fib(4))#__call__
print(fib(5))
print(fib(6))
print(fib(100))
print(fib(4))
print(fib[4])#__getitem__
for x in fib:
    print(x,end=' ')

 

上下文管理 

上下文管理对象

 

class Point:
    def __init__(self):
        print('init')
    
    def __enter__(self):
        print('enter')
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('exit')

with Point() as f:
    print('do sth')

上下文管理的安全性

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

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

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('exit')

with Point() as f:#虽然抛出异常但是__enter__和__exit__都执行了
    raise Exception('error')
    print('do sth')

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

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

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('exit')

with Point() as f:#虽然抛出异常但是__enter__和__exit__都执行了
    # raise Exception('error')
    sys.exit()
    print('do sth')

print('outer')

#init
#enter
#exit

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:#虽然抛出异常但是__enter__和__exit__都执行了
    print(p == f)#为什么不相等?
    print('do sth')

print('outer')

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

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

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('exit')
p = Point()
with p as f:#虽然抛出异常但是__enter__和__exit__都执行了
    print(p == f)#修改__enter__返回值后相等
    print('do sth')

print('outer')

 

__enter__方法和__exit__方法的参数

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

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

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('exit')
        print(exc_type)#<class 'Exception'>
        print(exc_val)#New Error
        print(exc_tb)#<traceback object at 0x00000000029FD508>
        return True#此时会压制异常,即不会抛出异常
p = Point()
with p as f:
    raise Exception('New Error')
    print('do sth')
    
#输出如下:
init
enter
exit
<class 'Exception'>
New Error
<traceback object at 0x00000000029FD688>

练习

import time
import datetime
from functools import wraps
#装饰器实现
def timeit(fn):
    @wraps(fn)
    def wrapper(*args,**kwargs):
        start = datetime.datetime.now()
        ret = fn(*args,**kwargs)
        delta = (datetime.datetime.now() -start).total_seconds()
        print('{} took {}\'s  装饰器'.format(fn.__name__,delta))
        return ret
    return wrapper

@timeit
def add(x,y):
    time.sleep(1)
    return x + y


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

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

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

with TimeIt(add):
    add(4,5)

# add took 1.000057's 装饰器
# add took 1.000057's 上下文

 

def add(x,y):
    time.sleep(1)
    return x + y

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

    def __enter__(self):
        self.start = datetime.datetime.now()
        return self
        # return self.fn        #方法一:实质上还是调用add函数

    def __call__(self, *args,**kwargs):
        return self.fn(*args,**kwargs)

    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))
    pass

with TimeIt(add) as foo:
    print(foo(4,5))    #self(4,5)  利用魔术方法__call__

 

#类装饰器的实现
import time
import datetime
from functools import wraps

class TimeIt:
    def __init__(self,fn):
        self.fn = fn
        wraps(fn)(self)

    def __call__(self, *args,**kwargs):
        start = datetime.datetime.now()
        ret = self.fn(*args,**kwargs)
        delta = (datetime.datetime.now() -start).total_seconds()
        print('{} took {}\'s  类装饰器'.format(self.fn.__name__,delta))
        return ret

@TimeIt
def add(x,y):
    """This is a function"""
    time.sleep(1)
    return x + y

add(10,11)#add took 1.000057's  类装饰器
print(add.__doc__)#This is a function
print(type(add))#<class '__main__.TimeIt'>

上下文应用场景

contextlib.contextmanager

import contextlib

@contextlib.contextmanager
def foo():  #
    print('enter')  #l类比于__enter__()
    yield   #yield的值只能有一个,作为__enter__方法的返回值
    print('exit')   #类比于__exit__()

with foo() as f:
    # raise Exception()
    print(f)

import contextlib

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

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

 

import contextlib
import datetime
import time

@contextlib.contextmanager
def add(x,y):  #为生成器函数增加了上下文管理
    start = datetime.datetime.now()
    try:
        yield x + y   #yield的值只能有一个,作为__enter__方法的返回值
    finally:
        delta = (datetime.datetime.now()-start).total_seconds()
        print(delta)

with add(4,5) as f:
    # raise Exception()
    time.sleep(1)
    print(f)
#输出如下:
# 9
# 1.000058

 

@functools.total_ordering装饰器

from functools import total_ordering

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

    @property
    def age(self):
        return self._age

    def __eq__(self, other):#必须
        return self._age == other.age

    def __lt__(self, other):#多选一
        return self._age < other.age

p1 = Person('20')
p2 = Person('30')
if p1<p2:
    print('p1 older')
else:
    print('p2 older')

反射

概述

反射相关的函数和方法

 

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

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

    def show(self):
        print(self.x,self.y)

p  =Point(4,5)
print(p)
print(p.__dict__)
p.z = 10
print(p.__dict__)
print(dir(p))

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)

p1  =Point(4,5)
p2  =Point(10,10)
print(repr(p1),repr(p2),sep='\n')
print(p1.__dict__)
setattr(p1,'y',16)
setattr(p1,'z',10)
print(getattr(p1,'__dict__'))
#动态调用方法
# if hasattr(p1,'show'):
#     print(getattr(p1,'show'))#<bound method Point.show of <__main__.Point object at 0x00000000029C3D68>>
#     getattr(p1,'show')()

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

print(Point.add)
print(p1.add)
print(p1.add(p2))#绑定

#为实例增加方法,未绑定
if not hasattr(p1,'sub'):
    setattr(p1,'sub',lambda self,other:Point(self.x-other.x,self.y-other.y))

print(p1.sub(p1,p2))
print(p1.sub)

#add在谁里面,sub在谁里面
print(p1.__dict__)
print(Point.__dict__)

练习

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

    def reg(self,cmd,fn):
        if isinstance(cmd,str):
            # setattr(self.__class__,cmd,fn)
            setattr(type(self),cmd,fn)
        else:
            print('error')

    def run(self):
        while True:
            cmd = input('Please input command:')
            if cmd.strip() == 'quit':
                return
            getattr(self,cmd.strip(),self.defaultfn)()

    def defaultfn(self):
        print('default')

dis  = dispatcher()
print(dis.__dict__)#{}
print(dispatcher.__dict__)#{'__module__': '__main__', 'cmd1': <function dispatcher.cmd1 at 0x00000000029A10D0>,...}
#添加注册函数
dis.reg('cmd2',lambda self:print(2))
dis.reg('cmd3',lambda self:print(3))
#启动
dis.run()

反射相关的魔术方法

__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):
        print( "missing {}".format(item))

p1 = Point(4,5)
print(p1.x)#4
print(p1.z)#6
print(p1.n)#0
print(p1.t) #missing t      None

__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):
        print( "missing {}".format(item))

    def __setattr__(self, key, value):
        print("__setattr__",key,value)

p1 = Point(4,5)
print(p1.__dict__)#{}
print(Point.__dict__)#{'__module__': '__main__', 'z': 6,...}
print(p1.x)#missing x       None
print(p1.z)#6
print(p1.n)#0
print(p1.t) #missing t      None
p1.__dict__['x'] = 60
print(p1.__dict__)#{'x': 60}
print(p1.x)#60

 __delattr__()

class Base:
    n = 0

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

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

p = Point(14,5)
del p.x
p.z = 15
del p.z
del p.Z
print(Point.__dict__)#{'__module__': '__main__', 'Z': 6, '__init__':...}
print(p.__dict__)#{'x': 14, 'y': 5, 'z': 15}
del Point.Z
print(Point.__dict__)#{'__module__': '__main__', '__init__': ...}

 

 __getattribute__

class Base:
    n = 5

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

    def __getattribute__(self, item):
        return item

    def __getattr__(self, item):
        print( "missing {}".format(item))

p1 = Point(4,5)
print(1,p1.__dict__)#__dict_
print(2,p1.x)#x
print(3,p1.z)#z
print(4,p1.n)#n
print(5,p1.t)#t
print(6,p1.__dict__)#__dict__
print(7,Point.z)#6

class Base:
    n = 5

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

    def __getattribute__(self, item):
        print('getattribute!!')
        return object.__getattribute__(self,item)
        # return item
    #
    def __getattr__(self, item):
        print( "missing {}".format(item))

p1 = Point(4,5)
print('*'*10)
print(1,p1.__dict__)#getattribute!!         1 {'x': 4, 'y': 5}
print(2,p1.x)#getattribute!!                2 4
print(3,p1.z)#getattribute!!                3 6
print(4,p1.n)#getattribute!!                4 5
print(5,p1.t)#getattribute!!     missing t  5 None
print('*'*10)
print(6,p1.__dict__)#getattribute!!         6 {'x': 4, 'y': 5}
print(7,Point.z)#                           7 6
# print(8,Point.zzzz)#AttributeError: type object 'Point' has no attribute 'zzzz'

 总结

描述器Descriptors

描述器的表现

 

 

class A:
    def __init__(self):
        print('A init')
        self.a1 = 'a1'

class B:
    x = A()
    def __init__(self):
        print('B init')

print('-'*20)
print(B.x.a1)
print('='*20)
b = B()
print(b.x.a1)

#输出结果:
A init
--------------------
a1
====================
B init
a1

class A:
    def __init__(self):
        print('A init')
        self.a1 = 'a1'

    def __get__(self, instance, owner):
        print("A.__get__ {} {} {}".format(self,instance,owner))

class B:
    x = A()
    def __init__(self):
        print('B init')

print('-'*20)
# print(B.x.a1)报错
print(B.x)
print('='*20)
b = B()
print(b.x)
# print(b.x.a1)报错
#输出如下: A init -------------------- A.__get__ <__main__.A object at 0x00000000029D3D68> None <class '__main__.B'> None ==================== B init A.__get__ <__main__.A object at 0x00000000029D3D68> <__main__.B object at 0x00000000029D3D30> <class '__main__.B'> None

 

 

class A:
    def __init__(self):
        print('A init')
        self.a1 = 'a1'

    def __get__(self, instance, owner):
        print("A.__get__ {} {} {}".format(self,instance,owner))
        return self #解决返回None的问题

class B:
    x = A()
    def __init__(self):
        print('B init')
        self.x = A()    #实例属性也指向一个A的实例

print('-'*20)
print(B.x.a1)
print('='*20)
b = B()
print(b.x.a1)
#输出如下:
A init
--------------------
A.__get__ <__main__.A object at 0x0000000002993DD8> None <class '__main__.B'>
a1
====================
B init
A init
a1

 

描述器定义

 

属性的访问顺序

class A:
    def __init__(self):
        print('A init')
        self.a1 = 'a1'

    def __get__(self, instance, owner):
        print("A.__get__ {} {} {}".format(self,instance,owner))
        return self #解决返回None的问题

class B:
    x = A()
    def __init__(self):
        print('B init')
        self.x = 'b.x'    #增加实例属性x

print('-'*20)
# print(B.x.a1)报错
print(B.x.a1)
print('='*20)
b = B()
print(b.x)
# print(b.x.a1)报错
#输出如下:
A init
--------------------
A.__get__ <__main__.A object at 0x00000000022A3D68> None <class '__main__.B'>
a1
====================
B init
b.x

class A:
    def __init__(self):
        print('A init')
        self.a1 = 'a1'

    def __get__(self, instance, owner):
        print("A.__get__ {} {} {}".format(self,instance,owner))
        return self #解决返回None的问题

    # 设置了该函数代表了一个数据描述器,而数据描述器的属性查找顺序优先于实例的__dict__
    def __set__(self, instance, value):
        print("A.__set__{} {} {}".format(self,instance,value))
        # self.data = value

class B:
    x = A()
    def __init__(self):
        print('B init')
        self.x = 'b.x'    #增加实例属性x

print('-'*20)
# print(B.x)
print(B.x.a1)#A.__get__ <__main__.A object at 0x000000000299D278> None <class '__main__.B'>     a1
print('='*20)
b = B()#A.__set__<__main__.A object at 0x000000000299D278> <__main__.B object at 0x000000000299D2B0> b.x
print(b.x)#A.__get__ <__main__.A object at 0x000000000299D278> <__main__.B object at 0x000000000299D2B0> <class '__main__.B'>   <__main__.A object at 0x000000000299D278>
print(b.x.a1)#返回a1

本质(进阶)

class A:
    def __init__(self):
        print('A init')
        self.a1 = 'a1'

    def __get__(self, instance, owner):
        print("A.__get__ {} {} {}".format(self,instance,owner))
        return self

    # 设置了该函数代表了一个数据描述器,而数据描述器的属性查找顺序优先于实例的__dict__
    def __set__(self, instance, value):
        print("A.__set__{} {} {}".format(self,instance,value))
        # self.data = value

class B:
    x = A()
    def __init__(self):
        print('B init')
        self.x = 'b.x'    #增加实例属性x
        self.y = 'b.y'

print('-'*20)
b = B()
print(b.x)
# print(b.x.a1)#未屏蔽__set__()方法则返回a1
print(b.y)
print('字典:')
print(b.__dict__)
print(B.__dict__)

#屏蔽__set__()方法
# 字典:
# {'x': 'b.x', 'y': 'b.y'}
# {'__module__': '__main__', 'x': <__main__.A object at 0x0000000002983D30>, '__init__': <function B.__init__ at 0x0000000003A0A048>,...}
#未屏蔽__set__()方法
# 字典:
# {'y': 'b.y'}
# {'__module__': '__main__', 'x': <__main__.A object at 0x000000000299D278>, '__init__': <function B.__init__ at 0x0000000003A0A0D0>,...}

Python中的描述器

class A:
    @classmethod
    def foo(cls):#非数据描述器
        pass

    @staticmethod#非数据描述器
    def bar():
        pass

    @property#数据描述器
    def z(self):
        return 5

    def getfoo(self):#非数据描述器
        return self.foo

    def __init__(self):#非数据描述器
        self.foo = 100
        self.bar = 200
        self.getfoo = 400
        # self.z = 300#AttributeError: can't set attribute

a = A()
print(a.__dict__)
print(A.__dict__)
#输出如下:
{'foo': 100, 'bar': 200, 'getfoo': 400}
{'__module__': '__main__', 'foo': <classmethod object at 0x00000000029B3DA0>, 'bar': <staticmethod object at 0x00000000029BD240>, 'z': <property object at 0x000000000047F818>, 'getfoo': <function A.getfoo at 0x0000000003A0A0D0>, '__init__': <function A.__init__ at 0x0000000003A0A158>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}

 

练习

#1
class StaticMethod:
    def __init__(self,fn):
        self.fn = fn

    def __get__(self, instance, owner):
        print(self,instance,owner)
        return self.fn

class A:
    @StaticMethod#foo = StaicMethod(foo),
   #也可以理解为foo重新指向了一个StaticMethod的实例,而StaticMethod类又实现了一个__get__方法,这样就形成了描述器。
    def foo():
        print('static')

f = A.foo
f()
#输出如下:
<__main__.StaticMethod object at 0x0000000002993E48> None <class '__main__.A'>
static
#2
from functools import partial
class ClassMethod:
    def __init__(self,fn):
        self.fn = fn

    def __get__(self, instance, cls):
        print(self, instance, cls)
        return partial(self.fn,cls)

class A:
    @ClassMethod#bar=ClassMethod(bar)
    def bar(cls):
        print(cls.__name__)
f = A.bar
f()
#输出如下:
<__main__.ClassMethod object at 0x00000000029055F8> None <class '__main__.A'>
A

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

#1
class Person:
    def __init__(self,name:str,age:int):
        params = ((name,str),(age,int))
        if not self.checkdata(params):
            raise TypeError()
        self.name = name
        self.age = age

    def checkdata(self,params):
        for p,t in params:
            if not isinstance(p,t):
                return False
        return True

p = Person('tom','20')

  

class Typed:
    def __init__(self,name,type):
        self.name = name
        self.type = type

    def __get__(self, instance, owner):
        pass

    def __set__(self, instance, value):
        if not isinstance(value,self.type):
            raise TypeError(value)
        instance.__dict__[self.name] = value

class Person:
    name = Typed('name',str)#不优雅
    age = Typed('age',int)#不优雅

    def __init__(self,name:str,age:int):
        self.name = name
        self.age = age

p = Person('tom',20)

class Typed:
    def __init__(self,name,type):
        self.name = name
        self.type = type

    def __get__(self, instance, owner):
        pass

    def __set__(self, instance, value):
        if not isinstance(value,self.type):
            raise TypeError(value)
        instance.__dict__[self.name] = value

import inspect
def typeassert(cls):
    params = inspect.signature(cls).parameters
    # print(params)
    for name,param in params.items():
        print(param.name,param.annotation)
        if param.annotation != param.empty:#注入类属性
            setattr(cls,name,Typed(name,param.annotation))
    return cls

@typeassert #Person = typeassert(Person)
class Person:
    # name = Typed('name',str)#装饰器注入+++++
    # age = Typed('age',int)#

    def __init__(self,name:str,age:int):
        self.name = name
        self.age = age

p = Person('tom',20)#   name <class 'str'>     age <class 'int'>
print(p.__dict__)   #   {'name': 'tom', 'age': 20}
print(p)            #   <__main__.Person object at 0x00000000023AD278>

作业

#进阶解法:
#实现双向链表
class SingleNode:
    #代表一个节点
    def __init__(self,val,next=None,prev=None):
        self.val = val
        self.next = next
        self.prev = prev

    def __repr__(self):
        return str(self.val)

class LinkedList:
    #容器类,某种方式存储一个个节点
    def __init__(self):
        self.head = None
        self.tail = None
        self.size = 0

    def append(self,val):
        node = SingleNode(val)
        if self.head is None:# 0
            self.head = node
        else:
            self.tail.next = node
            node.prev = self.tail
        self.tail = node

        self.size += 1

    def pop(self):
        if self.tail is None:#0
            raise NotImplementedError('Empty')
        tail = self.tail
        prev= self.tail.prev
        if prev is None:#1个节点
            self.head = None
            self.tail = None
        else:#>1
            self.tail = prev
            prev.next = None

        self.size -= 1
        return tail.val


    def insert(self,index,val):#1,7
        if index < 0:
            raise Exception('Error')
        cur = None
        for i,current in enumerate(self.iternodes()):
            if i == index:
                cur = current
                break

        if cur is None:#说明索引越界或空链表,直接末尾追加
            self.append(val)
            return

        node = SingleNode(val)
        prev = cur.prev
        if prev is None:#1个节点,头部插入
            self.head = node
            node.next = cur
            cur.prev = node
        else:#>=2
            node.next = cur
            prev.next = node
            cur.prev = node
            node.prev = prev

    self.size += 1
def remove(self,index): if self.tail is None: raise Exception('Empty') if index < 0: raise ValueError("Wrong Index()".format(index)) current = None for i,node in enumerate(self.iternodes()): if i == index: current = node break if current is None: raise ValueError('Wrong Index {}. Out of boundary'.format(index)) prev = current.prev next = current.next if prev is None and next is None:#only one node self.head = None self.tail = None elif prev is None: self.head = next next.prev = None elif next is None: self.tail = prev prev.next = None else: prev.next = next next.prev = prev del current self.size -= 1 def iternodes(self,reversed = False): current = self.tail if reversed else self.head while current: yield current current = current.prev if reversed else current.next __iter__ = iternodes def __getitem__(self, index): #index >= 0 for i,node in enumerate(self.iternodes(False if index>=0 else True),0 if index>=0 else 1): if i == abs(index): return node #index < 0 # for i,node in enumerate(self.iternodes(True),1): # if -i == index: # return node def __setitem__(self, key, value): self[key].val = value a = SingleNode(1) b = SingleNode(2) c = SingleNode(3) d = SingleNode(4) e = SingleNode(5) f = SingleNode(6) ll = LinkedList() ll.append(a) ll.append(b) ll.append(c) ll.append(d) ll.append(e) ll.append(f) # ll.insert(1,0) # ll.insert(0,0) # ll.insert(10,100) # print('pop元素:',ll.pop()) # print('pop元素:',ll.pop()) # print('pop元素:',ll.pop()) # ll.insert(0,10) for node in ll.iternodes(): print(node) print(ll[-2])#5 print(ll[2])#3

进阶题

 

#实现类property装饰器,类名称为Property
#基本结构如下,是一个数据描述器

class Property:
    def __init__(self, fget=None, fset=None, fdel=None):
        self.fget = fget
        self.fset = fset

    def __get__(self, instance, owner):
        if instance is not None:
            return self.fget(instance)
        return self

    def __set__(self, instance, value):
        self.fset(instance,value)
        # instance.__dict__['_data'] = value

    def setter(self,fn):
        self.fset = fn
        return self

class A:
    def __init__(self,data):
        self._data = data

    @Property#  data = Property(data)  data==>obj
    def data(self):
        return self._data

    @data.setter#   data = data.setter(data)    data==>obj
    def data(self,value):
        self._data = value

a = A(10)
print(a.data)
a.data = 100
print(a.data)
posted @ 2019-04-19 22:28  小鲨鱼~  阅读(437)  评论(0编辑  收藏  举报