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]: typeIt 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 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 ️.
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 .