5.元类编程

一.两种魔法方法

1.__getattr__方法

在类中调用属性时查找不到所要调用的属性时,执行该方法。

from datetime import date


class User:
    def __init__(self, name, birthday):
        self.name = name
        self.birthday = birthday

    def __getattr__(self, item):   #未找到属性时执行
        print('age not found')
        print(item)   #item即为未找到的属性名称


if __name__ == '__main__':
    user = User('kris', date(year=1999, month=2, day=6))      #格式化日期
    user.age       #没有找到age属性

打印结果:
在这里插入图片描述

2.__getattribute__方法(属性拦截器)

每当调用属性时,都会先调用该方法(该方法作为了解):

from datetime import date


class User:
    def __init__(self, name, birthday):
        self.name = name
        self.birthday = birthday

    def __getattr__(self, item):   #未找到属性时执行
        print('age not found')
        print(item)   #item即为未找到的属性名称

    def __getattribute__(self, item):
        print('我最先执行!')
        # return object.__getattribute__(self,item)   #此处谨慎返回



if __name__ == '__main__':
    user = User('kris', date(year=1999, month=2, day=6))      #格式化日期
    user.age    #没有找到age属性
    user.name

打印如下:
在这里插入图片描述

二.属性描述符

属性描述符是指Python中实现了__get__()、__set __()、__delete __()方法中的任意一个方法的类。
属性描述符可以便捷地验证同一类的属性

class IntField:
    
    #任意实现其一即为属性描述符
    
    #instance为调用该描述符的实例对象,owner为调用该描述符的类对象
    def __get__(self, instance, owner): 
        pass
    
     #instance为调用该描述符的实例对象,value为所设置的属性值
    def __set__(self, instance, value):
        pass

    def __delete__(self, instance):
        pass

1.数据描述符
实现了__get__()和__set__()方法的属性描述符:

#定义一个判断设置属性是否为整数的描述符
class IntField:
    #访问属性
    def __get__(self, instance, owner):
        print('__get__')
        return self.values

    #设置属性
    def __set__(self, instance, value):
        print('__set__')
        if not isinstance(value,int):   #如果不是整数,报错
            raise ValueError('ValueError')
        self.values = value


class User:
    age = IntField()    #将属性描述符作为User类的一个属性


user = User()
user.age = 18   #调用属性描述符,设置属性
print(user.age)     #调用属性描述符,访问属性

打印结果:
在这里插入图片描述

2.非数据描述符
只实现了__get__()方法的属性描述符:

class NoneDaTaInterField:
    def __get__(self, instance, owner):
        pass

3.属性查找顺序

user = User(), 那么user.age 顺序如下:

1 如果"age"是出现在User或其基类的__dict__中, 且age是data descriptor,那么调用其__get__方法, 否则

2 如果"age"出现在user的__dict__中, 那么直接返回 obj.dict[‘age’],否则

3 如果"age"出现在User或其基类的__dict__中

3.1 如果age是non-data descriptor,那么调用其__get__方法, 否则

3.2 返回 dict[‘age’]

4 如果User有__getattr__方法,调用__getattr__方法,否则

5 抛出AttributeError

重点记住最先查找属性描述符中的__get__()方法!!!

三.自定义元类

1.动态创建类

#动态创建类
def create_class(name):
    if name == 'user':
        class User:
            def __str__(self):
                return 'user'
        return User       #将创建好的类的引用返回
    elif name == 'student':
        class Student:
            def __str__(self):
                return 'student'
        return Student

user = create_class('user')    #调用函数,动态创建一个user类
print(user)
print(user())    #创建实例对象,调用__str__方法

打印如下:

2.使用元类创建类
由于Python语言的所有东西都是对象,因此类也是一个对象,同样可以将其像实例方法一样创建出来。

元类:创建类的类。type()函数除了可以查看对象所属类型之外,它还是一个类,一个创建类的类:

print(type(type))    #<class 'type'>

type的源码注释中这样写道:type(name, bases, dict) -> a new type 。
因此,我们可以用type来动态创建类,具体如下:

#其中类名为字符串,继承的父类为元组形式,属性方法以字典形式传入
A = type('A', (), {})  
print(A)    #<class '__main__.A'>

实例运用:

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


def act(self):
    print('playing basketball')

#用type动态创建一个类
Student = type('Student', (Info,), {'grade': 5, 'act': act})

kris = Student('kris',12)
print(kris.age)    #调用继承的父类中的age属性,打印: 12
print(kris.grade)    #调用类中的grade属性,打印: 5
kris.act()    #调用类中的act方法,打印:  playing basketball

3.metaclass属性

metaclass属性作为创建类似的参数,对其赋值后,Python会以元类的方式创建一个类并可控制该类的行为。

现在我们来使用metaclass属性创建一个类,使该类的所有属性大写:

#定义一个可改变创建的类的行为的方法,返回元类
def upper_attr(class_name, class_parents, class_attr):
    new_attr = {}
    for name,value in class_attr.items():
        if not name.startswith('_'):
            new_attr[name.upper()] = value
        else:
            new_attr[name] = value
    print(new_attr) 


    return type(class_name, class_parents, new_attr)    #返回元类


#使用metaclass属性以元类的方式创建类,并附加了一个大写属性的功能
class A(object,metaclass=upper_attr):
    name = 'kris'


kris = A()
print(kris)
print(kris.NAME)

打印结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值