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

Explore the ancestor of Python classes -- metaclasses

編輯:Python

Python Everything is an object

Python It is an object-oriented language , therefore Python Middle number 、 character string 、 list 、 aggregate 、 Dictionaries 、 function 、 Classes are objects .

utilize type() Check it out. Python Object types in

In [11]: # Numbers
In [12]: type(10)
Out[12]: int
In [13]: type(3.1415926)
Out[13]: float
In [14]: # character string
In [15]: type('a')
Out[15]: str
In [16]: type("abc")
Out[16]: str
In [17]: # list
In [18]: type(list)
Out[18]: type
In [19]: type([])
Out[19]: list
In [20]: # aggregate
In [21]: type(set)
Out[21]: type
In [22]: my_set = {1, 2, 3}
In [23]: type(my_set)
Out[23]: set
In [24]: # Dictionaries
In [25]: type(dict)
Out[25]: type
In [26]: my_dict = {'name': 'hui'}
In [27]: type(my_dict)
Out[27]: dict
In [28]: # function
In [29]: def func():
...: pass
...:
In [30]: type(func)
Out[30]: function
In [31]: # class
In [32]: class Foo(object):
...: pass
...:
In [33]: type(Foo)
Out[33]: type
In [34]: f = Foo()
In [35]: type(f)
Out[35]: __main__.Foo
In [36]: # type
In [37]: type(type)
Out[37]: type

It can be seen that

  • Numbers 1 yes int type The object of
  • character string abc yes str type The object of
  • list 、 aggregate 、 The dictionary is type type The object of , The created objects belong to list、set、dict type
  • function func yes function type The object of
  • Custom class Foo Created objects f yes Foo type , The class itself Foo It is type type The object of .
  • even type In itself type Object of type

1. Class is also an object

A class is a collection of objects that have equal functions and the same properties

In most programming languages , A class is a set of code snippets that describe how to generate an object . stay Python This is still true :

In [1]: class ObjectCreator(object):
...: pass
...:
In [2]: my_object = ObjectCreator()
In [3]: print(my_object)
<__main__.ObjectCreator object at 0x0000021257B5A248>

however ,Python And there's more to it than that . A class is also an object . Yes , you 're right , It's the object . As long as you Use keywords class,Python The interpreter creates an object as it executes .

The following code snippet :

>>> class ObjectCreator(object):
… pass
…

An object will be created in memory , The name is ObjectCreator. This object ( Class object ObjectCreator) Owning the create object ( Instance object ) The ability of . however , Its essence is still an object , So you can do the following with it :

  1. You can assign it to a variable
  2. You can copy it
  3. You can add properties to it
  4. You can pass it as a function argument

The following example :

In [39]: class ObjectCreator(object):
...: pass
...:
In [40]: print(ObjectCreator)
<class '__main__.ObjectCreator'>
In [41]:# Pass as parameter
In [41]: def out(obj):
...: print(obj)
...:
In [42]: out(ObjectCreator)
<class '__main__.ObjectCreator'>
In [43]: # hasattr To determine whether a class has some properties
In [44]: hasattr(ObjectCreator, 'name')
Out[44]: False
In [45]: # New class properties
In [46]: ObjectCreator.name = 'hui'
In [47]: hasattr(ObjectCreator, 'name')
Out[47]: True
In [48]: ObjectCreator.name
Out[48]: 'hui'
In [49]: # Assign a class to a variable
In [50]: obj = ObjectCreator
In [51]: obj()
Out[51]: <__main__.ObjectCreator at 0x212596a7248>
In [52]:

2. Create classes dynamically

Because classes are also objects , You can create them dynamically at runtime , Just like any other object . First , You can create classes in functions , Use class Key words can be used .

def cls_factory(cls_name):
"""
Create a class factory
:param: cls_name Create the name of the class
"""
if cls_name == 'Foo':
class Foo():
pass
return Foo # The class is returned , Not an instance of a class
elif cls_name == 'Bar':
class Bar():
pass
return Bar

IPython test

MyClass = cls_factory('Foo')
In [60]: MyClass
Out[60]: __main__.cls_factory.<locals>.Foo # The function returns a class , Not an instance of a class
In [61]: MyClass()
Out[61]: <__main__.cls_factory.<locals>.Foo at 0x21258b1a9c8>

But that's not dynamic enough , Because you still have to code the entire class yourself . Because classes are also objects , So they have to be generated by something, right .

When you use class When a keyword ,Python The interpreter creates this object automatically . But just like Python Most things are the same ,Python It still gives you a way to do it manually .

3. Use type Create a class

type There is a completely different function , Create classes dynamically .

type You can accept a description of a class as an argument , Then return a class .( Need to know , Depending on the parameters passed in , It is silly to have two completely different USES for the same function , But that is Python In order to maintain backward compatibility )

type It could work like this :

type( Class name , A tuple consisting of a parent class name ( In the case of inheritance , Can be null ), A dictionary containing attributes ( Name and value ))

Consider the following code :

In [63]: class Test:
...: pass
...:
In [64]: Test()
Out[64]: <__main__.Test at 0x21258b34048>
In [65]:

You can create it manually like this :

In [69]:# Use type Defining classes
In [69]: Test2 = type('Test2', (), {})
In [70]: Test2()
Out[70]: <__main__.Test2 at 0x21259665808>

We use Test2 As the name of the class , You can also use it as a variable as a reference to a class . Classes and variables are different , There is no reason to complicate things here . namely type function pass the civil examinations 1 An argument , It could be called something else , This name represents the name of the class

In [71]: UserCls = type('User', (), {})
In [72]: print(UserCls)
<class '__main__.User'>
In [73]:

Use help To test this 2 Classes

In [74]: # use help see Test class
In [75]: help(Test)
Help on class Test in module __main__:
class Test(builtins.object)
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
In [76]: # use help see Test2 class
In [77]: help(Test2)
Help on class Test2 in module __main__:
class Test2(builtins.object)
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
In [78]:

4. Use type Create a class with attributes

type Accept a dictionary to define properties for the class , therefore

Parent = type('Parent', (), {'name': 'hui'})

Can be translated as :

class Parent(object):
name = 'hui'

And you can put Parent Use as a normal class :

In [79]: Parent = type('Parent', (), {'name': 'hui'})
In [80]: print(Parent)
<class '__main__.Parent'>
In [81]: Parent.name
Out[81]: 'hui'
In [82]: p = Parent()
In [83]: p.name
Out[83]: 'hui'

Of course , You can inherit this class , The code is as follows :

class Child1(Parent):
name = 'jack'
sex = ' male '
class Child2(Parent):
name = 'mary'
sex = ' Woman '

I can write this as :

 Child1 = type('Child1', (Parent, ), {'name': 'jack', 'sex': ' male '})
In [85]: Child2 = type('Child2', (Parent, ), {'name': 'mary', 'sex': ' Woman '})
In [87]: Child1.name, Child1.sex
Out[87]: ('jack', ' male ')
In [88]: Child2.name, Child2.sex
Out[88]: ('mary', ' Woman ')

Be careful :

  • type Of the 2 Parameters , In the tuple is the name of the parent class , Instead of strings
  • The added attribute is Class properties , It's not an instance property

5. Use type Create a class with a method

Eventually you'll want to add methods to your class . Just define a function with the proper signature and assign it as a property .

Add instance method

In [89]: Parent = type('Parent', (), {'name': 'hui'})
In [90]: # Defined function
In [91]: def get_name(self):
...: return self.name
...:
In [92]: Child3 = type('Child3', (Parent, ), {'name': 'blob', 'get_name': get_name})
In [93]: c3 = Child3()
In [94]: c3.name
Out[94]: 'blob'
In [95]: c3.get_name()
Out[95]: 'blob'

Add static methods

In [96]: Parent = type('Parent', (), {'name': 'hui'})
In [97]: # Define static methods
In [98]: @staticmethod
...: def test_static():
...: print('static method called...')
...:
In [100]: Child4 = type('Child4', (Parent, ), {'name': 'zhangsan', 'test_static': test_static})
In [101]: c4 = Child4()
In [102]: c4.test_static()
static method called...
In [103]: Child4.test_static()
static method called...

Add class method

In [105]: Parent = type('Parent', (), {'name': 'hui'})
In [106]: # Define class methods
In [107]: @classmethod
...: def test_class(cls):
...: print(cls.name)
...:
In [108]: Child5 = type('Child5', (Parent, ), {'name': 'lisi', 'test_class': test_class})
In [109]: c5 = Child5()
In [110]: c5.test_class()
lisi
In [111]: Child5.test_class()
lisi

You can see , stay Python in , Class is also an object , You can create classes dynamically . This is it. When you use keywords class when Python Behind the scenes , It's implemented through metaclasses .

More complete use type The way classes are created :

class Animal(object):
def eat(self):
print(' Eat something ')
def dog_eat(self):
print(' I like to eat bones ')
def cat_eat(self):
print(' I like fish ')
Dog = type('Dog', (Animal, ), {'tyep': ' Mammals ', 'eat': dog_eat})
Cat = type('Cat', (Animal, ), {'tyep': ' Mammals ', 'eat': cat_eat})
# ipython test
In [125]: animal = Animal()
In [126]: dog = Dog()
In [127]: cat = Cat()
In [128]: animal.eat()
Eat something
In [129]: dog.eat()
I like to eat bones
In [130]: cat.eat()
I like fish 

6. What exactly is a metaclass ( Finally, it's the theme )

Metaclasses are used to create classes 【 thing 】. You create a class to create an instance object of the class , isn't it? ? But we've learned that Python Is also an object .

Metaclasses are used to create these classes ( object ) Of , Metaclasses are classes of classes , You can think of it this way :

MyClass = MetaClass() # Use the metaclass to create an object , This object is called “ class ”
my_object = MyClass() # Use “ class ” To create the instance object 

You've seen it type You can do it like this :

MyClass = type('MyClass', (), {})

That's because the function type It's actually a metaclass .type Namely Python The metaclass behind which all classes are created . Now you want to know why type It's all lowercase instead of Type Well ? ok , I guess it's for the sum str consistency ,str Is the class used to create a string object , and int Is the class used to create an integer object .type That's the class that creates the class object . You can pass the inspection __class__ Property to see that . therefore Python Everything is an object

Now? , For any one __class__ Of __class__ What are properties ?

In [136]: a = 10
In [137]: b = 'acb'
In [138]: li = [1, 2, 3]
In [139]: a.__class__.__class__
Out[139]: type
In [140]: b.__class__.__class__
Out[140]: type
In [141]: li.__class__.__class__
Out[141]: type
In [142]: li.__class__.__class__.__class__
Out[142]: type

therefore , Metaclasses are the things that create objects like classes .type Namely Python The inner metaclass of , Yes, of course , You can also create your own metaclass .

7. __metaclass__ attribute

You can add to a class when you define it __metaclass__ attribute .

class Foo(object):
__metaclass__ = something…
... Omit ...

If you do ,Python You will use metaclasses to create classes Foo. Be careful , There are some tricks here . You write down first class Foo(object), But the class Foo Not created in memory yet .Python It looks in the definition of the class __metaclass__ attribute , If you find it ,Python I'm going to use it to create classes Foo, If not found , It's built in type To create this class .

class Foo(Bar):
pass

Python We did the following :

  1. Foo There is __metaclass__ Student: this property ? If there is ,Python Will pass __metaclass__ Create a name for Foo Class ( object )
  2. If Python Can't find __metaclass__, It will continue to be Bar( Parent class ) Search for __metaclass__ attribute , And try to do the same thing .
  3. If Python It is not found in any parent class __metaclass__, It's going to look in the module level __metaclass__, And try to do the same thing .
  4. If I still can't find it __metaclass__ ,Python It's built in type To create this class object .

The problem now is , You can __metaclass__ What code is placed in ?

The answer is : You can create something of a class . So what can be used to create a class ?type, Or any use of type Or subclassized type Fine .

8. Custom metaclasses

The main purpose of metaclasses is to automatically change classes when they are created .

Imagine a silly example , You decide that all class properties in your module should be capitalized . There are several ways to do this , But one way is by setting it at the module level __metaclass__. In this way , All classes in this module are created through this metaclass , All we need to do is tell the metaclass to capitalize all of its properties .

Fortunately, ,__metaclass__ It can actually be called anywhere , It does not need to be a formal class . therefore , Let's start with a simple function .

python2 in

# -*- coding:utf-8 -*-
def upper_attr(class_name, class_parents, class_attr):
# class_name The name of the class is saved Foo
# class_parents Saves the parent of the class object
# class_attr All class properties are stored in a dictionary
# Traversal property Dictionary , It's not __ The first attribute name becomes uppercase
new_attr = {}
for name, value in class_attr.items():
if not name.startswith("__"):
new_attr[name.upper()] = value
# call type To create a class
return type(class_name, class_parents, new_attr)
class Foo(object):
__metaclass__ = upper_attr # Set up Foo The metaclass of class is upper_attr
bar = 'bip'
print(hasattr(Foo, 'bar'))
# Flase
print(hasattr(Foo, 'BAR'))
# True
f = Foo()
print(f.BAR)

python3 in

# -*- coding:utf-8 -*-
def upper_attr(class_name, class_parents, class_attr):
# Traversal property Dictionary , It's not __ The first attribute name becomes uppercase
new_attr = {}
for name,value in class_attr.items():
if not name.startswith("__"):
new_attr[name.upper()] = value
# call type To create a class
return type(class_name, class_parents, new_attr)
# Inheritance of subclasses () Use in metaclass
class Foo(object, metaclass=upper_attr):
bar = 'bip'
print(hasattr(Foo, 'bar'))
# Flase
print(hasattr(Foo, 'BAR'))
# True
f = Foo()
print(f.BAR)

Do it again , This time use a real one class Think of it as a metaclass .

class UpperAttrMetaClass(type):
def __new__(cls, class_name, class_parents, class_attr):
# Traversal property Dictionary , It's not __ The first attribute name becomes uppercase
new_attr = {}
for name, value in class_attr.items():
if not name.startswith("__"):
new_attr[name.upper()] = value
# Method 1: adopt 'type' To create class objects
return type(class_name, class_parents, new_attr)
# Method 2: Reuse type.__new__ Method
# This is the basic OOP Programming , There's no magic
# return type.__new__(cls, class_name, class_parents, new_attr)
# python3 Usage of
class Foo(object, metaclass=UpperAttrMetaClass):
bar = 'bip'
# python2 Usage of
class Foo(object):
__metaclass__ = UpperAttrMetaClass
bar = 'bip'
print(hasattr(Foo, 'bar'))
# Output : False
print(hasattr(Foo, 'BAR'))
# Output : True
f = Foo()
print(f.BAR)
# Output : 'bip'
__new__ Is in __init__ The special method that was called earlier
__new__ Is a method used to create an object and return it
and __init__ It is simply used to initialize the incoming parameter to the object
here , The objects that are created are classes , We want to be able to customize it , So let's rewrite this __new__

this is it , besides , There is really nothing else to say about metaclasses . But in terms of the metaclass itself , They're actually quite simple :

  1. Creation of the interceptor class
  2. Modify the class
  3. Returns the modified class

Why use metaclasses at all ?

Now back to our big theme , Why on earth would you use such an error-prone and obscure feature ?

ok , Generally speaking , You can't use it :

“ Metaclasses are deep magic ,99% Users should not have to worry about it at all . If you want to figure out whether you need to use metaclasses or not , Then you don't need it . Those who actually use metaclasses know exactly what they need to do, right , And you don't even have to explain why you use metaclasses, right .” —— Python The leader of the world Tim Peters

Source code

The source code has been uploaded to GiteePythonKnowledge: Python The treasure house of knowledge , Welcome to visit .

It's not easy to code words , I hope you can support me a lot ️.

official account

New folder X

It took nature tens of billions of years to create our real world , And programmers use hundreds of years to create a completely different virtual world . We use the keyboard to make bricks and tiles , Build everything with your brain . People put 1000 Regard as authority , We do the opposite , defend 1024 The status of . We're not keyman , We are just the extraordinary creators of the ordinary world .


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