Python类-元编程Meta programming, since 2022-01-30

(2022.01.30 Sun 除夕前夜)
开发者总要铭记: DRY, i.e., Don’t Repeat Yourself。代码复用成为关键环节。

在Python中使用元编程Meta programming解决复用问题。Meta programming is the concept of building functions or classes whose primary target is to manipulate code by modifying, wrapping or generating existing codes.元编程就是用已有的代码控制和生成新的代码。

元编程的几个典型特征:

  • 元类Metaclass
  • 装饰器Decorator
  • 类装饰器class decorator

元编程的优点是debug被隔离在特定位置,方便了debug,也方便了修改和disable。

先来看看python中的类和对象。

type、类和对象

Python中有一句话是 Everything is an object万物皆对象。函数,类,内置类型,都是对象。而类/对象来自于何处呢?

我们来验证类和对象的类型

>>> class SomeClass:
...     pass
... 
>>> a = SomeClass()
>>> type(a)

>>> type(SomeClass)

>>> type(type)

inspect工具判断对象是否为类,或者非类

>>> import inspect
>>> inspect.isclass(SomeClass)
True
>>> inspect.isclass(a)
False
>>> inspect.isclass(type)
True

从上面的结果可以看到一个类的实例,其类型是该类。该类的类型是typetype的类型是其本身,并且type本身也是一个类。

>>> isinstance(a, SomeClass)
True
>>> isinstance(a, type)
False
>>> isinstance(SomeClass, type)
True
>>> isinstance(type, type)
True

# 2022.03.16 Wed, also available to other types, e.g., str, float and etc
>>> age = 5
>>> age.__class__

>>> age.__class__.__class__

一个实例是实例化的类,一个类是实例化的元类metaclass,也就是type。(2022.03.17 Thur)在面向对象的编程中,元类的实例还是类。

type本身是一个类,是Python内置的metaclass元类。我们可以自定义metaclass,所有类和元类都从type继承而来。元类是Python元编程的方式之一。

type-meta-class.jpg

type创建类

type是Python中类的元类,因此可用type创建类。创建格式如下

= type(, [], {: method_name_1, : var_value, ...})

如,创建一个叫classBase的类,没有内置方法和变量

>>> classBase = type('classBase', (), {}) 
# 第二个参数只能是tuple格式,其中数值为空表示继承自type,
# 第三个是字典格式,用于标记类方法和类变量

classBase类创建其他类(非实例化),即继承。注意,如果通过type创建的类没有实例化,则定义语句外的方法声明中不需要含有self参数。

>>> def func(argu1, argu2):
>>>    return 'something'
>>> newClass = type('newClass', (classBase,), {'method1': func, 'var': 5})
# 第二个参数别忘记逗号
>>> print(newClass.var) 
5
>>> print(newClass.method1(1, 2))
'something'

list类定义一个新类,该类不仅有list的内置方法,还可以自定义类变量和类方法。注意,通过type定义的类,一旦其被实例化,自定义的方法声明中需要有self参数。

>>> def func(self, arg1):
...     return str(arg1)+' is a string.'
... # 定义这个函数时需要加self参数
>>> newList = type('newList', (list, ), {'var': 99, 'display': func})
>>> a = newList() # instantiation
>>> type(a)

>>> a.append(1)
>>> print(a)
[1]
>>> a.var
99
>>> a.display('bbb')
'bbb is a string.'

(2022.03.16 Wed)
type创建/继承类,其优点在于可以“动态”的创建类。比如上面的newList对象,如果按常规的类创建方式可以写成如下形式。修改方法或添加方法都需要重写类。

class newList:
    var = 99
    def __init__(self):
        pass
    def display(self, arg1):
        return str(arg1)+' is a string.'

(2022.03.17 Thur)
也可以自己定义元类,自定义元类是type类的继承。下面例子展示了如何自定义元类,该自定义元类为继承它的子类创建了hello方法,子类无需定义即可继承该方法。

class helloMeta(type): # 这里有对type类的继承
    def hello(cls):
        print('helloMeta class')
    def __call__(self, *args, **kwargs):
        cls = type.__call__(self, *args)
        setattr(cls, 'hello', self.hello)
        return cls

class tryHello(object, metaclass=helloMeta): #这里的两个参数必不可少
    def greet(self):
        self.hello()

调用

>>> t = tryHello()
>>> t.greet()
helloMeta class

typeobject的区别

(2022.05.18 Wed)
object是所有类的父类,type也是继承了object类。在Python3.x中,所有类的创建默认情况下,即class cn:语句,都是从object继承类,只有标注了type才是从type继承类,如class cn(type):。相比继承自type的类,继承自object的类内置方法更少。

通过__mro__方法或mro可以查看一个类的继承顺序/方法解析顺序(Method Resolution Order)。可以看到如果类继承自type类,会最终继承object类。

class a(object):
    pass
class b:
    pass
class c(type):
    pass
>> a.__mro__
(__main__.a, object)
>> b.__mro__
(__main__.b, object)
>> c.__mro__
(__main__.c, type, object)
>> dir(a)
['__class__',  '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
 '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__',
 '__lt__', '__module__', '__ne__', '__new__',
 '__reduce__', '__reduce_ex__', '__repr__', '__setattr__',
 '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
>> dir(c)
['__abstractmethods__', '__base__', '__bases__', '__basicsize__',
 '__call__', '__class__', '__delattr__', '__dict__',
 '__dictoffset__', '__dir__', '__doc__', '__eq__',
 '__flags__', '__format__', '__ge__', '__getattribute__',
 '__gt__', '__hash__', '__init__', '__init_subclass__',
 '__instancecheck__', '__itemsize__', '__le__', '__lt__',
 '__module__', '__mro__', '__name__', '__ne__',
 '__new__', '__prepare__', '__qualname__', '__reduce__',
 '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
 '__str__', '__subclasscheck__',  '__subclasses__', '__subclasshook__',
 '__text_signature__', '__weakrefoffset__', 'mro']

object类的类型是type

>> type(a)
type

元类的应用场景1: 实现单例模式

(2022.03.16 Wed)
我们用元类形式实现单例模式。另外,装饰器也可实现单例模式。

class Singleton(type): #这里一定要有type关键字表明其继承元类
    def __init__(cls, name, bases, dic):
        super(Singleton, cls).__init__(name, bases, dic)
        cls.instance = None
    def __call__(cls, *args, **kwargs):
        if cls.instance is None:
            print('creating a new instance')
            cls.instance = super(Singleton, cls).__call__(*args, **kwargs)
        else:
            print('warning: only one instance allowed')
        return cls.instance
class MySingleton(metaclass=Singleton):
    pass

实例化

>>> a = MySingleton()
creating a new instance
>>> b = MySingleton()
warning: only one instance allowed
>>> c = MySingleton()
warning: only one instance allowed

元类的应用场景2: 创建APIs

(2022.03.16 Wed)

>>> from django.db import models
>>> class Vehicle(models.Model):
       color = models.CharField(max_length=10)
       wheels = models.IntegerField()

接下来可以实例化Vehicle这个类

>>> car = Vehicle(color="Blue", wheels=4)
>>> car.wheels
4

元类的应用,比如Django ORM,Flask,SQLAlchemy和AOP等。

类在创建时到底发生了什么

(2022.02.25 Fri)
Python程序的三个基础block分别是

  • Statements
  • Functions
  • Classes

类的命名空间被保存成字典

class demo:
    var1 = 1
    def __init__(self):
        self.value = 100
>>> demo.__dict__
mappingproxy({'__dict__': ,
              '__doc__': None,
              '__init__': ,
              '__module__': '__main__',
              '__weakref__': ,
              'var1': 1})
>>> a = demo()
>>> a.__dict__
{'value': 100}

当一个类被创建的时候,发生了如下几步

  • 类的本体被隔离 The body (statements and methods) of the class is isolated.
  • 类的命名空间字典被创建,但不赋值 The namespace dictionary of the class is created, not populated though.
  • 执行类的本体,类命名空间字典被赋值 The body of the class executes, then the namespace dictionary is populated with all of the attributes, methods defined, and some additional useful info about the class.
  • 元类在基类中被标记出 The metaclass is identified in the base classes or the metaclass hooks (explained later) of the class to be created.
  • 用类名和属性值实例化元类 The metaclass is then called with the name, bases, and attributes of the class to instantiate it.

装饰器Decorator

(2022.02.10 Thur)
参考Python装饰器

类装饰器Class decorator

(2022.02.25 Fri)
参考Python装饰器

Reference

1 https://developer点ibm点com/tutorials/ba-metaprogramming-python/
2 realpython com primer-on-python-decorators (https冒号//realpython点com/primer-on-python-decorators/)
3 编写高质量代码 改善Python编程的91个建议,张颖等著,机械工业出版社

你可能感兴趣的:(Python类-元编程Meta programming, since 2022-01-30)