程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

Python from door to mastery (8): metaclass-01-metaclass

編輯:Python

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 .

One 、 Control class creation

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 ;

  1. __prepare__( Defining classes )
  2. __new__( Before instance creation )
  3. __init__( When an instance is created )
  4. __call__( When an instance is created )

1.1、 Basics

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
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

1.2、 Single case

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')
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

Two 、 Type of control

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)
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
class
Spam(
metaclass
=
MyMeta,
debug
=
True,
synchronize
=
True):

pass

class Spam( metaclass = MyMeta):
debug = True
synchronize = True
pass
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

3、 ... and 、 Control properties

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)
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.

Four 、 application

4.1、 Framework class

4.1.1、 Force type signature

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)} ')
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.

4.1.2、 Mandatory encoding specification

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
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.

4.1.3、 Control initialization

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))
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
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
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

4.2、 Code related

4.2.1、 General attribute definition method

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
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.

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
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.

4.2.2、 Define context manager

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")
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.

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')
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved