Python之旅的第29天(property补充、元类和自定义元类)

主要是两部分内容,感觉最近记性不咋好,可能是睡少了

一、property的补充

# 关于property,我们昨天用类的修饰器和数据描述符做了自我实现,那么今天再补充一些系统中的property的方法
# 关于get、set、delete三个
class Test:
    @property
    def test(self):
        print('get method is congming')
    @test.setter
    def test(self,value):  # 这里是应该注意的,当你需要对其重新设定一个数值的时候必然会传递一个新的数值作为test的新值,
                           # 所以比如要增加一个参数选项
        print('set method is coming')
    @test.deleter
    def test(self):
        print('delete method is coming')
# 使用Test类生成实例化对象之后,我们是没办法对静态属性test进行操作的,
# 我们没办法对其进行修改和删除
# 但是引入了@property就是可以的
f1 = Test()
f1.test
# get method is congming
print(f1.__dict__)   #{}
# 如果没有定义setter方法,肯定也是不能对其进行修改的
f1.test = 'alex'
# set method is coming
print(f1.__dict__)   #{}之所以是空,是因为我们定义的方法啥事没干
del f1.test
# delete method is coming

# 上面已经明白了大致原理了,那么接下来就可以尝试一下他的实际方法了
# 以商场购物打折为例吧
class Goods:

    def __init__(self):
        # 原价
        self.original_price = 100
        # 折扣
        self.discount = 0.8

    @property
    def price(self):
        # 实际价格 = 原价 * 折扣
        new_price = self.original_price * self.discount
        return new_price

    @price.setter
    def price(self, value):
        self.original_price = value

    @price.deleter
    def price(self):
        del self.original_price


obj = Goods()
obj.price         # 获取商品价格
obj.price = 200   # 修改商品原价
print(obj.price)
del obj.price     # 删除商品原价

# 同时也可以用于实现类的输入数据的类型检查
#实现类型检测功能

#第一关:
class People:
    def __init__(self,name):
        self.name=name

    @property
    def name(self):
        return self.name

# p1=People('alex') #property自动实现了set和get方法属于数据描述符,比实例属性优先级高,所以你这面写会触发property内置的set,抛出异常


#第二关:修订版

class People:
    def __init__(self,name):
        self.name=name #实例化就触发property

    @property
    def name(self):
        # return self.name #无限递归
        print('get------>')
        return self.DouNiWan

    @name.setter
    def name(self,value):
        print('set------>')
        self.DouNiWan=value

    @name.deleter
    def name(self):
        print('delete------>')
        del self.DouNiWan

p1=People('alex') #self.name实际是存放到self.DouNiWan里
print(p1.name)
print(p1.name)
print(p1.name)
print(p1.__dict__)

p1.name='egon'
print(p1.__dict__)

del p1.name
print(p1.__dict__)


#第三关:加上类型检查
class People:
    def __init__(self,name):
        self.name=name #实例化就触发property

    @property
    def name(self):
        # return self.name #无限递归
        print('get------>')
        return self.DouNiWan

    @name.setter
    def name(self,value):
        print('set------>')
        if not isinstance(value,str):
            raise TypeError('必须是字符串类型')
        self.DouNiWan=value

    @name.deleter
    def name(self):
        print('delete------>')
        del self.DouNiWan

p1=People('alex') #self.name实际是存放到self.DouNiWan里
p1.name=1

二、元类和自定制元类

# 元类
# python中一切皆为对象。
# 所有的对象都是实例化或者说调用类而得到的(调用类的过程称为类的实例化)
# 基于python中一切皆为对象的概念分析出:我们用class关键字定义的类本身也是一个对象,
# 负责产生该对象的类称之为元类(元类可以简称为类的类),内置的元类为type
# class关键字在帮我们创建类时,必然帮我们调用了元类OldboyTeacher=type(...),那调用type时传入的参数是什么呢?必然是类的关键组成部分,一个类有三大组成部分,分别是
# 1、类名class_name='OldboyTeacher'
# 2、基类们class_bases=(object,)
# 3、类的名称空间class_dic,类的名称空间是执行类体代码而得到的
# 调用type时会依次传入以上三个参数
#一个类没有声明自己的元类,默认他的元类就是type,除了使用内置元类type,
# 我们也可以通过继承type来自定义元类,然后使用metaclass关键字参数为一个类指定元
class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
    pass

class OldboyTeacher(object,metaclass=Mymeta): # OldboyTeacher=Mymeta('OldboyTeacher',(object),{...})
    school='oldboy'

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

    def say(self):
        print('%s says welcome to the oldboy to learn Python' %self.name)
# 自定义元类可以控制类的产生过程,类的产生过程其实就是元类的调用过程,即OldboyTeacher=Mymeta('OldboyTeacher',(object),{...}),
# 调用Mymeta会先产生一个空对象OldoyTeacher,然后连同调用Mymeta括号内的参数一同传给Mymeta下的__init__方法,完成初始化,于是我们
class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
    def __init__(self,class_name,class_bases,class_dic):
        # print(self) #<class '__main__.OldboyTeacher'>
        # print(class_bases) #(<class 'object'>,)
        # print(class_dic) #{'__module__': '__main__', '__qualname__': 'OldboyTeacher', 'school': 'oldboy', '__init__': <function OldboyTeacher.__init__ at 0x102b95ae8>, 'say': <function OldboyTeacher.say at 0x10621c6a8>}
        super(Mymeta, self).__init__(class_name, class_bases, class_dic)  # 重用父类的功能

        if class_name.islower():
            raise TypeError('类名%s请修改为驼峰体' %class_name)

        if '__doc__' not in class_dic or len(class_dic['__doc__'].strip(' \n')) == 0:
            raise TypeError('类中必须有文档注释,并且文档注释不能为空')

class OldboyTeacher(object,metaclass=Mymeta): # OldboyTeacher=Mymeta('OldboyTeacher',(object),{...})
    """
    类OldboyTeacher的文档注释
    """
    school='oldboy'

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

    def say(self):
        print('%s says welcome to the oldboy to learn Python' %self.name)

# 关于__call__方法,其实也是 type类给我们写的一个方法
# 实际情况是这样的
lass MyType(type):
    def __init__(self,a,b,c):
        print('元类的构造函数执行')
        # print(a)
        # print(b)
        # print(c)
    def __call__(self, *args, **kwargs):
        # print('=-======>')
        # print(self)
        # print(args,kwargs)
        obj=object.__new__(self) #object.__new__(Foo)-->f1
        self.__init__(obj,*args,**kwargs)  #Foo.__init__(f1,*arg,**kwargs)
        return obj
class Foo(metaclass=MyType): #Foo=MyType(Foo,'Foo',(),{})---》__init__
    def __init__(self,name):
        self.name=name #f1.name=name

# 或者
class MyType(type):
    def __init__(self,a,b,c):
        print('元类的构造函数执行')
    def __call__(self, *args, **kwargs):
        obj=object.__new__(self)
        self.__init__(obj,*args,**kwargs)
        return obj
class Foo(metaclass=MyType):
    def __init__(self,name):
        self.name=name
f1=Foo('alex')

就是这些,今天还早,可以好好睡觉了。

posted @ 2020-03-22 22:22  崆峒山肖大侠  阅读(171)  评论(0编辑  收藏  举报