当前位置 : 主页 > 编程语言 > python >

​​Python从门到精通(八):元类-01-元类

来源:互联网 收集:自由互联 发布时间:2022-06-30
本章主要说一点高级的编程技巧,python中也算是比较常用的一种技巧--元类。有点类似于拦截器或AOP的功能。 一、控制类的创建 对象的类型叫作类,类的类型叫作元类。实例对象由类创

本章主要说一点高级的编程技巧,python中也算是比较常用的一种技巧--元类。有点类似于拦截器或AOP的功能。

一、控制类的创建

对象的类型叫作类,类的类型叫作元类。实例对象由类创建,而类则是由元类创建。类进行创建时会优先执行以下几个方法,通过复写这几个方法就可以控制类的创建过程,规范化代码,然后在类定义时使用metaclass来定义行为,以下三个方法会按顺序执行;

  • __prepare__(定义类)
  • __new__(实例创建之前)
  • __init__(实例创建时)
  • __call__(实例创建时)
  • 1.1、基础

    class NoInstances(type):
    def __call__(self, *args, **kwargs):
    raise TypeError("Can't instantiate directly")

    # Example,这里最主要的是metacclass的运用
    class Spam(metaclass=NoInstances):
    @staticmethod
    def grok(x):
    print('Spam.grok')

    Spam.grok(30)
    s = Spam() #会报错

    1.2、单例

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

    def __call__(self, *args, **kwargs):
    if self.__instance is None:
    self.__instance = super().__call__(*args, **kwargs)
    return self.__instance
    else:
    return self.__instance

    # Example
    class Spam(metaclass=Singleton):
    def __init__(self):
    print('Creating Spam')

    二、控制类型

    from abc import ABCMeta, abstractmethod
    class IStream(metaclass=ABCMeta):
    @abstractmethod
    def read(self, maxsize=None):
    pass

    @abstractmethod
    def write(self, data):
    pass


    class MyMeta(type):
    # Optional
    @classmethod
    def __prepare__(cls, name, bases, *, debug=False, synchronize=False):
    # Custom processing
    pass
    return super().__prepare__(name, bases)

    # Required
    def __new__(cls, name, bases, ns, *, debug=False, synchronize=False):
    # Custom processing
    pass
    return super().__new__(cls, name, bases, ns)

    # Required
    def __init__(self, name, bases, ns, *, debug=False, synchronize=False):
    # Custom processing
    pass
    super().__init__(name, bases, ns)class Spam(metaclass=MyMeta, debug=True, synchronize=True):
    pass

    class Spam(metaclass=MyMeta):
    debug = True
    synchronize = True
    pass

    三、控制属性

    from collections import OrderedDict

    # A set of descriptors for various types
    class Typed:
    _expected_type = type(None)
    def __init__(self, name=None):
    self._name = name

    def __set__(self, instance, value):
    if not isinstance(value, self._expected_type):
    raise TypeError(f'Expected {str(self._expected_type)}')
    instance.__dict__[self._name] = value

    class Integer(Typed):
    _expected_type = int

    class Float(Typed):
    _expected_type = float

    class String(Typed):
    _expected_type = str

    # Metaclass that uses an OrderedDict for class body
    class OrderedMeta(type):
    def __new__(cls, cls_name, bases, cls_dict):
    d = dict(cls_dict)
    order = []
    for name, value in cls_dict.items():
    if isinstance(value, Typed):
    value._name = name
    order.append(name)
    d['_order'] = order
    return type.__new__(cls, cls_name, bases, d)

    @classmethod
    def __prepare__(cls, cls_name, bases):
    return OrderedDict()


    class Structure(metaclass=OrderedMeta):
    def as_csv(self):
    return ','.join(str(getattr(self,name)) for name in self._order)

    # Example use
    class Course(Structure):
    course_name = String()
    total_class = Integer()
    score = Float()

    def __init__(self, course_name, total_class, score):
    self.course_name = course_name
    self.total_class = total_class
    self.score = score


    course = Course('python', 30, 0.3)
    print(f'course name: {course.course_name}')
    print(f'course as csv: {course.as_csv()}')
    # err_ = Course('python','total class', 0.3)


    from collections import OrderedDict

    class NoDupOrderedDict(OrderedDict):
    def __init__(self, cls_name):
    self.cls_name = cls_name
    super().__init__()
    def __setitem__(self, name, value):
    if name in self:
    raise TypeError(f'{name} already defined in {self.cls_name}')
    super().__setitem__(name, value)

    class OrderedMeta(type):
    def __new__(cls, cls_name, bases, cls_dict):
    d = dict(cls_dict)
    d['_order'] = [name for name in cls_dict if name[0] != '_']
    return type.__new__(cls, cls_name, bases, d)

    @classmethod
    def __prepare__(cls, cls_name, bases):
    return NoDupOrderedDict(cls_name)

    四、应用

    4.1、框架类

    4.1.1、强制类型签名

    from inspect import Signature, Parameter
    parm_list = [Parameter('x', Parameter.POSITIONAL_OR_KEYWORD),
    Parameter('y', Parameter.POSITIONAL_OR_KEYWORD, default=42),
    Parameter('z', Parameter.KEYWORD_ONLY, default=None)]
    sig = Signature(parm_list)
    print(f'sig is: {sig}')


    def func(*args, **kwargs):
    bound_values = sig.bind(*args, **kwargs)
    for name, value in bound_values.arguments.items():
    print(f'name is: {name}, value is: {value}')

    func(1, 2, z=3)
    func(1)
    func(1, z=3)
    func(y=2, x=1)
    # func(1, 2, 3, 4)
    # func(y=2)
    # func(1, y=2, x=3)


    from inspect import Signature, Parameter

    def make_sig(*names):
    parm_list = [Parameter(name, Parameter.POSITIONAL_OR_KEYWORD)
    for name in names]
    return Signature(parm_list)

    class Structure:
    __signature__ = make_sig()
    def __init__(self, *args, **kwargs):
    bound_values = self.__signature__.bind(*args, **kwargs)
    for name, value in bound_values.arguments.items():
    setattr(self, name, value)

    class Course(Structure):
    __signature__ = make_sig('course_name', 'total_class', 'score')

    class Point(Structure):
    __signature__ = make_sig('x', 'y')


    import inspect
    print(f'Course signature: {inspect.signature(Course)}')
    course_1 = Course('python', 30, 0.3)
    # course_2 = Course('python', 30)
    # course_3 = Course('python', 30, 0.3, total_class=30)


    from inspect import Signature, Parameter

    def make_sig(*names):
    parms = [Parameter(name, Parameter.POSITIONAL_OR_KEYWORD)
    for name in names]
    return Signature(parms)

    class StructureMeta(type):
    def __new__(cls, cls_name, bases, cls_dict):
    cls_dict['__signature__'] = make_sig(*cls_dict.get('_fields',[]))
    return super().__new__(cls, cls_name, bases, cls_dict)

    class Structure(metaclass=StructureMeta):
    _fields = []
    def __init__(self, *args, **kwargs):
    bound_values = self.__signature__.bind(*args, **kwargs)
    for name, value in bound_values.arguments.items():
    setattr(self, name, value)

    class Course(Structure):
    _fields = ['course_name', 'total_class', 'score']

    class Point(Structure):
    _fields = ['x', 'y']


    import inspect
    print(f'course signature: {inspect.signature(Course)}')
    print(f'point signature: {inspect.signature(Point)}')

    4.1.2、强制编码规范

    class MyMeta(type):
    def __new__(cls, cls_name, bases, cls_dict):
    # cls_name is name of class being defined
    # bases is tuple of base classes
    # cls_dict is class dictionary
    return super().__new__(cls, cls_name, bases, cls_dict)


    class MyMeta(type):
    def __init__(self, cls_name, bases, cls_dict):
    super().__init__(cls_name, bases, cls_dict)
    # cls_name is name of class being defined
    # bases is tuple of base classes
    # cls_dict is class dictionary


    class Root(metaclass=MyMeta):
    pass

    class A(Root):
    pass

    class B(Root):
    pass


    class NoMixedCaseMeta(type):
    def __new__(cls, cls_name, bases, cls_dict):
    for name in cls_dict:
    if name.lower() != name:
    raise TypeError('Bad attribute name: ' + name)
    return super().__new__(cls, cls_name, bases, cls_dict)

    class Root(metaclass=NoMixedCaseMeta):
    pass

    class A(Root):
    def foo_bar(self):
    pass

    class B(Root):
    def fooBar(self):
    pass


    from inspect import signature
    import logging

    class MatchSignaturesMeta(type):

    def __init__(self, cls_name, bases, cls_dict):
    super().__init__(cls_name, bases, cls_dict)
    sup = super(self, self)
    for name, value in cls_dict.items():
    if name.startswith('_') or not callable(value):
    continue
    # Get the previous definition (if any) and compare the signatures
    # prev_dfn = getattr(sup,name,None)
    if (prev_dfn := getattr(sup,name,None)):
    prev_sig = signature(prev_dfn)
    val_sig = signature(value)
    if prev_sig != val_sig:
    logging.warning(f'Signature mismatch in {value.__qualname__}. {prev_sig} != {val_sig}')

    # Example
    class Root(metaclass=MatchSignaturesMeta):
    pass

    class A(Root):
    def foo(self, x, y):
    pass

    def spam(self, x, *, z):
    pass

    # Class with redefined methods, but slightly different signatures
    class B(A):
    def foo(self, a, b):
    pass

    def spam(self,x,z):
    pass

    4.1.3、控制初始化

    import operator

    class StructTupleMeta(type):
    def __init__(cls, *args, **kwargs):
    super().__init__(*args, **kwargs)
    for n, name in enumerate(cls._fields):
    setattr(cls, name, property(operator.itemgetter(n)))

    class StructTuple(tuple, metaclass=StructTupleMeta):
    _fields = []
    def __new__(cls, *args):
    if len(args) != len(cls._fields):
    raise ValueError(f'{len(cls._fields)} arguments required')
    return super().__new__(cls,args)


    class Course(StructTuple):
    _fields = ['course_name', 'total_class', 'score']

    class Point(StructTuple):
    _fields = ['x', 'y']


    course = Course('python', 30, 0.3)
    print(f'course is: {course}')
    print(f'course[0] = {course[0]}')
    print(f'course.course_name = {course.course_name}')
    print(f'course total_score = {course.total_class * course.score}')

    course.total_class = 20


    course = Course('python', 30, 0.3)
    # course = Course(('python', 30, 0.3))course is: ('python', 30, 0.3)
    course[0] = python
    course.course_name = python
    course total_score = 9.0
    Traceback (most recent call last):
    File "/Users/liudong/PycharmProjects/pythonProject/app/chapter9/init_cls.py", line 30, in <module>
    course.total_class = 20
    AttributeError: can't set attribute

    4.2、编码相关

    4.2.1、通用的属性定义方法

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

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

    @name.setter
    def name(self, value):
    if not isinstance(value, str):
    raise TypeError('name must be a string')
    self._name = value

    @property
    def age(self):
    return self._age

    @age.setter
    def age(self, value):
    if not isinstance(value, int):
    raise TypeError('age must be an int')
    self._age = value

    等价如下,但不是太建议

    def typed_property(name, expected_type):
    storage_name = '_' + name

    @property
    def prop(self):
    return getattr(self, storage_name)

    @prop.setter
    def prop(self, value):
    if not isinstance(value, expected_type):
    raise TypeError(f'{name} must be a {expected_type}')
    setattr(self, storage_name, value)

    return prop

    class Person:
    name = typed_property('name', str)
    age = typed_property('age', int)

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

    4.2.2、定义上下文管理器

    from contextlib import contextmanager
    import time
    @contextmanager
    def time_use(label):
    print("1")
    start = time.time()
    try:
    yield
    finally:
    end = time.time()
    print(f'{label}: {end - start} s')#4

    with time_use('counting'):
    print("2")
    n = 10000000
    while n > 0:
    n -= 1
    print("3")

    等价如下:

    class time_use:
    def __init__(self, label):
    self.label = label

    def __enter__(self):
    self.start = time.time()

    def __exit__(self, exc_ty, exc_val, exc_tb):
    end = time.time()
    print(f'{self.label}: {end - self.start} s')

    上一篇:Python的数据类型都有哪些?如何使用?
    下一篇:没有了
    网友评论