【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的东东都可以。

抽象函数

实现方式有3种

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

 

 

posted @ 2019-01-09 23:50  周sir搞人工  阅读(231)  评论(0编辑  收藏  举报