【python 第9日】上下文 类装饰器 元类 属性描述符扩展
with 上下文管理
包含__enter__/__exit__的模块
class WithTest: def __init__(self, name): print("这里是初始化") self.name = name def __enter__(self): print("这里是%s的enter" % self.name) return self def __exit__(self, exc_type, exc_val, exc_tb): print("这里是%s的exit" % self.name) print("exc_type", exc_type) print("exc_val", exc_val) print("exc_tb", exc_tb) return True with WithTest("zgl") as f: print(f.name) print(sss) print("---------->")
输出如下 这里是初始化 这里是zgl的enter zgl 这里是zgl的exit exc_type <class 'NameError'> exc_val name 'sss' is not defined exc_tb <traceback object at 0x003FDE40>
- 首先Withtest("zgl"),如果这里出错,上下文管理器是管理不到的,始终会报错,除非再外层加try/except
- 然后进入__enter__, 要return self 否则f接受到的是None类型
- 然后开始执行,上下文里面的内容,对于里面的变量赋值,外层函数也可以用,比如a =1 在最外层也是可以用的
- 如果报错,立刻进入__exit__, exc_type返回的异常类型, exc_val返回的是异常信息, exc_tb返回的是异常栈
- 如果__exit__返回True,则不报错,进行控制,编译器不发出异常,如果不返回True,则编译器仍然报错
- 之后不在执行后面的代码,推出上下文管理
类装饰器
类装饰器定义
在类外加上装饰器,使其增加附加功能,返回类
#给每个类添加一个数据属性和一个函数属性 def Decorator(obj):# print(School.__dict__) #添加数据属性 obj.addr = "浙江省杭州市" def price(): pass #添加函数属性 obj.price = price return obj #一定返回的是类本身 @Decorator #相当于执行 School = Decorator(School) class School(): def __init__(self,name,price): self.name =name self.price =price #打印类的属性字典 print(School.__dict__)
#
{'__module__': '__main__', '__init__': <function School.__init__ at 0x035D8AE0>, '__dict__': <attribute '__dict__' of 'School' objects>, '__weakref__': <attribute '__weakref__' of 'School' objects>, '__doc__': None, 'addr': '浙江省杭州市', 'price': <function Decorator.<locals>.price at 0x035D8588>}
带参数的类装饰器
添加可变参数,易于管理,
#给每个类添加一个可变的数据属性 def Decorator(**kwargs): def add(obj): "添加数据属性" # print('调用外部函数的**kwargs',kwargs) for key,val in kwargs.items(): # 添加数据属性 setattr(obj,key,val) #不可以用obj.__dict__ 的修改操作,是不可变更字典类型 return obj # print("外部传入的参数为:",kwargs) return add @Decorator(addr = "浙江省杭州市",name ="浙江大学") #执行顺序:1.运行Decorator函数,先打印外部的传入的参数,返回add函数名;2.再执行School = add(School) class School(): def __init__(self,price): self.price =price @Decorator(addr = "湖北省",price =12000) class School1(): pass print(School.__dict__) print(School1.__dict__)
装饰器类
和上面不同,是把类作为装饰器,相当于类的初始化
做一个Property类,把类中的函数变成静态变量
class Property: def __init__(self, func): self.func = func def __get__(self, instance, owner): # print(self, instance, owner) # self 调用对象,也就是score实例, instance是test实例 owner归属的类Test if not instance: return self #如果类调用返回对象 return self.func(instance) #实例调用这 def __set__(self, instance, value): # print(self, instance, value) instance.__dict__[ self.func.__name__ ] = value #修改实例test字典 class Test: def __init__(self, mark, discount): self.mark = mark self.discount = discount @Property #实例f = Property(score) 这里是类,数据描述符 def score(self): return self.mark * self.discount @property def score1(self): return self.mark * self.discount test = Test(10, 0.8) # print(Test.score1) #<property object at 0x0076AAE0> # print(Test.score) #<__main__.Property object at 0x00766090> # print(test.score1) #8.0 # print(test.score) #8.0 test.score = 1 print(test.__dict__) print(test.__dict__["score"]) print(test.score) #8.0 要想使用__dict__的,必须不加__set__, 在__get__里面修改 set(obj, name, value)
做一个classmethod类,把类中的方法变成类方法
本来想通过__call__实现,但是最后,不会获取函数的外层类,所以失败,如果只写Test,那太固定了,没意思,希望厉害的人回复,怎么获取函数的外层类,不是函数的类
class Classmethond: def __init__(self, func): self.func = func def __get__(self, instance, owner): def wrap(*args, **kwargs): return self.func(owner, *args, **kwargs) return wrap # def __call__(self, *args, **kwargs): # # print(self.cla , self, *args, **kwargs) # return self.func(self.cla , *args, **kwargs) #本来想通过__call__实现,但是最后,不会获取函数的外层类,所以失败,如果只写Test,那太固定了,没意思 class Test: x = 1 def __init__(self, mark, discount): self.mark = mark self.discount = discount @Classmethond #实例f = Property(score) 这里是类,数据描述符 def score(cls): print(cls, cls.x) @classmethod def score1(cls): print(cls, cls.x) Test.score1() #<class '__main__.Test'> 1 Test.score() #<class '__main__.Test'> 1 test = Test(1, 0.8) test.score1() #<class '__main__.Test'> 1 test.score() #<class '__main__.Test'> 1
做一个staticmethod 类,把类中的方法变成静态方法
class Staticmethod: def __init__(self, func): self.func = func def __get__(self, instance, owner): return self.func # def __call__(self, *args, **kwargs): # # print(self.cla , self, *args, **kwargs) # return self.func(self.cla , *args, **kwargs) class Test: x = 1 def __init__(self, mark, discount): self.mark = mark self.discount = discount @Staticmethod def score(): print("静态方法") @staticmethod def score1(): print("这是静态方法1") # Test.score1() Test.score1() #这是静态方法1 Test.score() #静态方法 test = Test(10, 0.8) test.score1() #这是静态方法1 test.score() #静态方法
元类 type 自定义(类名,父类,属性)
元类定义
元类就是用来创建类的“东西”。你创建类就是为了创建类的实例对象,不是吗?但是我们已经学习到了Python中的类也是对象。好吧,元类就是用来创建这些类(对象)的,元类就是类的类,
因此,元类就是创建类这种对象的东西。如果你喜欢的话,可以把元类称为“类工厂”(不要和工厂类搞混了:D) type就是Python的内建元类,当然了,你也可以创建自己的元类。
type(类名, 父类的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))
class Foo(object): __metaclass__ = something… […]
Python做了如下的操作:
Foo中有__metaclass__这个属性吗?如果是,Python会在内存中通过__metaclass__创建一个名字为Foo的类对象(我说的是类对象,请紧跟我的思路)。如果Python没有找到__metaclass__,它会继续在Bar(父类)中寻找__metaclass__属性,并尝试做和前面同样的操作。如果Python在任何父类中都找不到__metaclass__,它就会在模块层次中去寻找__metaclass__,并尝试做同样的操作。如果还是找不到__metaclass__,Python就会用内置的type来创建这个类对象。
现在的问题就是,你可以在__metaclass__中放置些什么代码呢?答案就是:可以创建一个类的东西。那么什么可以用来创建一个类呢?type,或者任何使用到type或者子类化type的东东都可以。
抽象函数
import abc class Abs(metaclass=abc.ABCMeta): # class Abs(object): # __metaclass__ = abc.ABCMeta #这个命名方式不行,不知道为什么 是python3 新修改的么 @abc.abstractmethod def abstar(self): pass print(Abs.__dict__) abcd = Abs()
自定义元类
class UpperAttrMetaclass(type): def __new__(cls, name, bases, dct): attrs = ((name, value) for name, value in dct.items() if not name.startswith('__')) uppercase_attr = dict((name.upper(), value) for name, value in attrs) return super(UpperAttrMetaclass, cls).__new__(cls, name, bases, uppercase_attr) class A(metaclass= UpperAttrMetaclass): pass
就是这样,除此之外,关于元类真的没有别的可说的了。使用到元类的代码比较复杂,这背后的原因倒并不是因为元类本身,而是因为你通常会使用元类去做一些晦涩的事情,依赖于自省,控制继承等等。确实,用元类来搞些“黑暗魔法”是特别有用的,因而会搞出些复杂的东西来。但就元类本身而言,它们其实是很简单的:
1) 拦截类的创建
2) 修改类
3) 返回修改之后的类
属性描述符、property(@setter,@deleter)和__setattr__
我这是么理解的,
- property用于的是少数属性的控制,1个2个
- 属性描述符用于的是某些的相同属性的控制 具有相同属性
- __setattr__ 全部属性的控制,当然也可以部分,不够代码结构复杂
__get__ __getAttribute__ ___getattr__ __getitem__ 顺序
- __get__使用在数据描述符中,暂时不考虑,后面三个的优先级顺序为__getitem__ > __getAttribute__ >___getattr__
- 对类属性A.x的获取不触发,对a.x的获取才触发
- 其他set del的顺序和这个一致,
- __del__是析构函数是在实例删除时候触发的
class A: data = 1 def __init__(self, id): self.id = id def __getattribute__(self, item): print("这里是getattribute",item) # raise AttributeError # print("这是getattribute后期") # return object.__getattribute__(self, item) return super(A, self).__getattribute__(item) def __getattr__(self, item): print("这里是getattr") def __getitem__(self, item): print("这里是getitem", item) # raise AttributeError return self.__dict__[item] # return object.__getattribute__(self, item) # if item in self.__dict__: # return self.__dict__[item] # else: # raise ValueError a = A("11") print(a.__dict__) #这里是getattribute __dict__ #{'id': '11'} print(a.data) #这里是getattribute data #1 print(a.x) #这里是getattribute x #这里是getattr #None print(a["x"]) #这里是getitem x #这里是getattribute __dict__ ###KeyError: 'x' print(a["data"]) #这里是getitem data #这里是getattribute __dict__ #KeyError: 'data' print(a["id"]) #这里是getitem id #这里是getattribute __dict__ #11
支付宝
您的资助是我最大的动力!
金额随意,欢迎来赏!
微信