This chapter mainly talks about some advanced programming skills ,python Is also a commonly used technique -- The metaclass . It's kind of like an interceptor or AOP The function of .
The type of an object is called a class , The types of classes are called metaclasses . Instance objects are created by classes , Classes are created by metaclasses . Class will give priority to the following methods , By copying these methods, you can control the creation process of the class , Normalized code , Then use... When defining classes metaclass To define behavior , The following three methods are executed in sequence ;
class NoInstances( type):
def __call__( self, * args, * * kwargs):
raise TypeError( "Can't instantiate directly")
# Example, The main thing here is metacclass The use of
class Spam( metaclass = NoInstances):
@ staticmethod
def grok( x):
print( 'Spam.grok')
Spam. grok( 30)
s = Spam() # Will report a mistake
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)
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)} ')
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
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
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
The equivalence is as follows , But not too much advice
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
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")
The equivalence is as follows :
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')