python元编程

1. 普通类

1.1 普通类的执行顺序

class Putong:
    def __new__(cls, *args, **kwargs):
        print("执行了 __new__ 方法。")
        new = super().__new__(cls, *args, **kwargs)
        return new                                        # 必须有返回值

    def __init__(self):
        print("执行了 __init__ 方法。")                      # 可以不用有返回值

    def __call__(self, arg):
        print(f"执行了 __call__ 方法,参数为:{arg}")         # 可以不用有返回值

p = Putong()
p('hello world')

执行结果为:

执行了 __new__ 方法。
执行了 __init__ 方法。
执行了 __call__ 方法,参数为:hello world

说明普通类实例化的执行顺序是先调用 new,再 init。

且自定义的new方法必须继承父类的new方法,还必须返回一个生成的类,才能调用 init 生成一个实例。

如果再对实例进行调用,就会调用类的 call 方法。

1.2 type与object的关系

print(issubclass(type, object))    # >>> True            :说明 object是type的父类。
print(type(object))				   # >>> <class 'type'>  :说明 object是type创建的。 
print(type(type))                  # >>> <class 'type'>  :说明type是由type自己创建的。
print(type.__bases__)             # >>> (<class 'object'>,)  :说明type的基类是object
print(object.__bases__)          # >>> ()   : 说明 object没有基类。

说明:object是由type创建的,但object又是type的父类。两者是一个互补的关系,type创建了object时,又继承了自己创建的object,type自身创建了type。

2. 元类

2.1 用type创建类

type既可以查看对象的类型,也可以用来创建对象。

type参数说明:

  • 第一个参数:要创建的类的名字,以字符串的形式传入。
  • 第二个参数:所继承的父类,如有多个父类,则以元组形式传入。
  • 第三个参数:类所关联的属性和方法。以字典的形式传入。
def init(self, name):
    self.name = name
    
@classmethod
def copy(cls, person):
    return cls(person.name)

# 创建类,第二个参数如果是继承自 Object的,则元组可以为空元组。 默认继承object,如第10行代码。
Person = type("person", (), {"desc": "有关人的个体的共性描述", "__init__": init, "copy": copy})  
# Person = type("person", (object,), {"desc":"有关人的个体的共性描述", "__init__":init})

# 实例化
p = Person("小红")
p1 = Person.copy(p)
print(p1.name)

2.2 自定义元类

元类是比类更抽象的类,用来生成类。

第一步:定义一个元类 和 一个使用元类的类。

#定义元类,需显示的继承 type 类。
class MyMetaClass(type):
	# 这里的第一个传入参数为类,所以用 cls。
    def __new__(cls, name, base, attrs):
        print("调用了元类的__new__方法")
        print(f"name={name},base={base},attrs={attrs}。")
        print(f"cls={cls}, type(cls)={type(cls)}")    
        # 因为__new__在父类type中是特殊的静态方法,所以继承时需要填入所有参数,不能省略cls。
        t = super(MyMetaClass, cls).__new__(cls, name, base, attrs)
        print(f"t={t}, type(t)={type(t)}")
        return t
    
    def __init__(cls, *args, **kwargs):
        print("调用了元类的 __init__ 方法。")
        print(f"__init__方法中的 cls为:{cls}")


# 指定元类创建类        
class A(metaclass=MyMetaClass):       # 默认为 metaclass=type
    pass

此时不创建A的实例,直接运行代码,结果为:

5 行: 调用了元类的__new__方法
6 行: name=A, base=(),  attrs={'__module__': '__main__', '__qualname__': 'A'}7 行: cls=<class '__main__.MyMetaClass'>,  type(cls)=<class 'type'>
10 行: t=<class '__main__.A'>,  type(t)=<class '__main__.MyMetaClass'>

14 行: 调用了元类的 __init__ 方法。
15 行: __init__方法中的 cls为:<class '__main__.A'>

说明只要定义了一个 A 类,就会调用 元类的 __new__( ) 和 __init__( ) 方法,

既如果再定义一个 B 类,那么就还会调用一次与B类相关的元类 __new__( ) 和 __init__( )方法。

def __new__(cls, name, base, attrs) 中参数说明:

  • name:类名,等同于上面type中的第一个参数。
  • base:继承的类,等同于上面type中的第二个参数。上面的A类因为没有继承任何类,所有base为空元组。
  • attrs:属性,等同于上面type中的第三个参数。

下面单独看下定义的 A 类内容:

print(f"A={A}, type(A)={type(A)}")

输出结果为:

A=<class '__main__.A'>,                     type(A)=<class '__main__.MyMetaClass'>

说明 类 A 就是元类 t = super().new(self, name, base, attrs) 的 t 。

第二步:实例化 A 类

实例化 A 类,就是对 A 的调用,既 A()

class MyMetaClass(type):

    def __new__(cls, name, base, attrs):
        print("调用了元类的__new__方法")
        print(f"name={name},base={base},attrs={attrs}。")
        print(f"self={cls}, type(self)={type(cls)}")
        # 因为__new__在父类type中是特殊的静态方法,所以继承时需要填入所有参数,不能省略cls。
        t = super(MyMetaClass, cls).__new__(cls, name, base, attrs)
        print(f"t={t}, type(t)={type(t)}")
        return t                           # 必须有返回才能调用 __call__ 方法。
    
    def __init__(cls, *args, **kwargs):    # >>> 这里的第一个传入参数为类,所以用 cls。
        print("调用了元类的 __init__ 方法。")
        print(f"__init__方法中的 cls为:{cls}")
    

    def __call__(cls, *args, **kwargs):
        print("调用了元类的__call__方法。")
        print(f"call中的self为:{cls}")              # >>> 这里的第一个传入参数为类,所以用 cls。
        return super(MyMetaClass, self).__call__(*args, **kwargs)    # 元类的call必须有返回值。


class A(metaclass=MyMetaClass):   # 注意,这是使用MyMetaClass为元类,而不是继承它。继承的是 object。

    def __call__(self, *args, **kwargs):
        print("调用了A类的__call__方法")
        print(f"A 类中call中的self为:{self}")   # >>> 这里的第一个传入参数为实例,所以用 self。


a = A()   #  实例化A类。A()会调用 元类 的 __call__() 方法。

对A加括号进行调用既A() ,就会生成一个实例。因为A()会调用元类的__call__() 方法。既创建实例,是通过元类的__call__( )方法创建的,_call_( )方法return的东西,就是 A()生成的东西。

如果再对a进行调用 a() ,则会调用自己实例的 __call__() 方法,既会输出 >>>:调用了A类的__call__方法

元类里 __call__( ) 第一个传入参数为类,所以用 __call__(cls)__ 。普通类里的call传入的是 实例,所以用 __call__(self)。

print(f"A={A}, type(A)={type(A)}")   
输出为:
>>> A=<class '__main__.A'>, type(A)=<class '__main__.MyMetaClass'>
       # 说明 A 是一个类,              说明 A 是通过  MyMetaClass 类创建的。
    

print(f"a={a}, type(A)={type(a)}")
输出为:
>>> a=<__main__.A object at 0x0000021D58DB1E20>, type(a)=<class '__main__.A'>
       # 说明 a 是一个实例,                            说明 a 是通过 A 类创建的。

3. 元编程例子

3.1 利用元类设置类的初始属性

# 自定义元类
class MyMetaClass(type):
    def __new__(cls, name, base, attrs, *args, **kwargs):
        
        # 创建类时会自动添加 create_time属性。
        attrs['create_time'] = datetime.now()           
        
        return super().__new__(cls, name, base, attrs, *args, **kwargs)

    
# 利用元类创建类
class A(metaclass=MyMetaClass):
    def __init__(self):
        print(f"__init__() 创建了一个A类 :  {datetime.now()}")

a = A()
print(f"自动添加的a.create_time属性: {a.create_time}")

运行值为:

__init__() 创建了一个A类 :  2021-05-05 23:01:33.793554
自动添加的a.create_time属性: 2021-05-05 23:01:33.793554

3.2 使用元类创建抽象类

抽象类: 只能被继承,无法实例化对象。

例子1:元类call方法返回None

class NoInstance(type):
    def __new__(mcs, *args, **kwargs):
        return super().__new__(mcs, *args, **kwargs)

    def __call__(cls, *args, **kwargs):
        # return super().__call__(*args, **kwargs)
        return None

class Abstract(metaclass=NoInstance):
    def __str__(self):
        return "这是一个抽象类。"

a = Abstract()
print(a, type(a))

运行结果为:

None <class 'NoneType'>

说明,此时的a是None实例,既为元类__call__()方法返回的值。

例子2:元类call方法raise Error

class NoInstance(type):
    def __new__(mcs, *args, **kwargs):
        return super().__new__(mcs, *args, **kwargs)

    def __call__(cls, *args, **kwargs):
        # return super().__call__(*args, **kwargs)
        raise TypeError("抽象类无法被实例化")

class Abstract(metaclass=NoInstance):
    def __str__(self):
        return "这是一个抽象类。"
    
a = Abstract()

此时 实例化 Abstract 就会报错。

例子3:直接调用静态方法和类方法

class NoInstance(type):
    def __new__(mcs, *args, **kwargs):
        return super().__new__(mcs, *args, **kwargs)

    def __call__(cls, *args, **kwargs):
        # return super().__call__(*args, **kwargs)
        raise TypeError("抽象类无法被实例化")

class Abstract(metaclass=NoInstance):
    @staticmethod
    def static():
        print("调用了静态方法。")

    @classmethod
    def cls_method(cls):
        print("调用了类方法。")

    def __str__(self):
        return "这是一个抽象类。"


# 虽然不能创建实例,但能直接调用静态方法和类方法。
Abstract.static()
Abstract.cls_method()

class B(Abstract):    # 可以被B类继承,此时的B类也无法创建对象。
    pass

# B类也能调用静态方法和类方法。
B.static()
B.cls_method()

3.3 final类/终极类

既生成不能被继承的类

内置函数 isinstance 两个作用说明:

  • isinstance(a, A): 可以判断实例a是不是由类A生成的。
  • isinstance(A, MyMetaclass):判断类A是不是由元类MyMetaclass创建。

利用这个特性,就可以创建一个无法被继承的类。

因为 元类 的 __new__(cls, name, base, attrs) 方法中 base 参数是一个元组,会放入将要创建类的父类。

代码演示:

class MyMetaclass(type):
    def __new__(cls, name, base, attrs):
        print(name, base)
        for i in base:
            if isinstance(i, MyMetaclass):      # 判断i是否由MyMetaclass创建。
                raise TypeError("当前类为终极类,不能被继承。")
        return super().__new__(cls, name, base, attrs)


class A(metaclass=MyMetaclass):
    pass

class B(A):
    pass

上面代码会运行两次元类的 new 方法,

既第一次A类调用new时 base 是空元组,

第二次B类调用new时,base是(<class ‘main.A’>,)

此时只要判断 base 中的元素是不是由 元类创建的,既可以设置是否可以被继承。

3.4 元类创建单例

既该类只能创建一个实例。

class MyMetaclass(type):
    def __init__(cls, *args, **kwargs):
        cls.__instance = None
        super().__init__(*args, **kwargs)

    def __call__(cls, *args, **kwargs):
        if not cls.__instance:        
            cls.__instance = super(MyMetaclass, cls).__call__(*args, **kwargs)
            return cls.__instance
        else:
            return cls.__instance

class A(metaclass=MyMetaclass):
    pass

a1 = A()
a2 = A()
print(id(a1))    # a1 和 a2 的id地址一样。
print(id(a2))

代码分析:

因为元类的init函数传入的第一个参数是类,而不是实例,所有此处用cls来区别self。

第7行 cls.__instance 判断类的私有属性 __instance 是否为空,如果为空,则 第8行代码 继承type的call方法,生成一个实例,然后把实例赋值给 cls.__instance 属性,再第9行返回该实例。

如果不为空,则第11行代码,直接返回 属性cls.__instance指向的实例。

小技巧,不用元类创建单例:

class A:
    __instance = None

    def __new__(cls, *args, **kwargs):
        if not cls.__instance:
            cls.__instance = super(A, cls).__new__(cls, *args, **kwargs)
        return cls.__instance

a1 = A()
a2 = A()
print(a1 is a2)

注:上面的元类 new 方法中的 if else 代码有点冗余,也可以这样优化:只需一个if,然后就return。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值