Flask接口签名sign原理与实例代码浅析
269
2022-08-26
Python从门到精通(八):元类-01-元类
本章主要说一点高级的编程技巧,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# Exampleclass Spam(metaclass=Singleton): def __init__(self): print('Creating Spam')
二、控制类型
from abc import ABCMeta, abstractmethodclass IStream(metaclass=ABCMeta): @abstractmethod def read(self, maxsize=None): pass @abstractmethod def write(self, data): passclass 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): passclass Spam(metaclass=MyMeta): debug = True synchronize = True pass
三、控制属性
from collections import OrderedDict# A set of descriptors for various typesclass 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] = valueclass Integer(Typed): _expected_type = intclass Float(Typed): _expected_type = floatclass String(Typed): _expected_type = str# Metaclass that uses an OrderedDict for class bodyclass 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 useclass 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 = scorecourse = 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 OrderedDictclass 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, Parameterparm_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, Parameterdef 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 inspectprint(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, Parameterdef 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 inspectprint(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 dictionaryclass Root(metaclass=MyMeta): passclass A(Root): passclass B(Root): passclass 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): passclass A(Root): def foo_bar(self): passclass B(Root): def fooBar(self): passfrom inspect import signatureimport loggingclass 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}')# Exampleclass Root(metaclass=MatchSignaturesMeta): passclass A(Root): def foo(self, x, y): pass def spam(self, x, *, z): pass# Class with redefined methods, but slightly different signaturesclass B(A): def foo(self, a, b): pass def spam(self,x,z): pass
4.1.3、控制初始化
import operatorclass 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 = 20course = Course('python', 30, 0.3)# course = Course(('python', 30, 0.3))
course is: ('python', 30, 0.3)course[0] = pythoncourse.course_name = pythoncourse total_score = 9.0Traceback (most recent call last): File "/Users/liudong/PycharmProjects/pythonProject/app/chapter9/init_cls.py", line 30, in
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 propclass 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 contextmanagerimport time@contextmanagerdef time_use(label): print("1") start = time.time() try: yield finally: end = time.time() print(f'{label}: {end - start} s')#4with 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')
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~