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
1
yes int type The object of abc
yes str type The object of list、set、dict
type func
yes function type The object of Foo
Created objects f
yes Foo
type , The class itself Foo
It is type type The object of .type
In itself type Object of type 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 :
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]:
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 .
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]:
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 :
Eventually you'll want to add methods to your class . Just define a function with the proper signature and assign it as a property .
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'
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...
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
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 .
__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 :
__metaclass__
Student: this property ? If there is ,Python Will pass __metaclass__
Create a name for Foo Class ( object )__metaclass__
, It will continue to be Bar( Parent class ) Search for __metaclass__
attribute , And try to do the same thing .__metaclass__
, It's going to look in the module level __metaclass__
, And try to do the same thing .__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 .
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 .
# -*- 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)
# -*- 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 :
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
The source code has been uploaded to Gitee
PythonKnowledge: Python The treasure house of knowledge , Welcome to visit .
It's not easy to code words , I hope you can support me a lot ️.
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 .