This is a very hot post on stack overflow. The questioner claimed to have mastered various concepts in Python OOP programming, but always felt that the metaclass was difficult to understand. He knows it's definitely about introspection, but still doesn't quite understand, and I hope you can give some practical examples and code snippets to help understand and under what circumstances you need metaprogramming. So e-satis students gave God General reply, the reply received 985 points of approval points, more people commented that this reply should be added to the official Python document. and E-satis classmate oneself in stack overflow prestige integral also up to 64271 points. Here is a wonderful reply (hint: very long)
Class is also an object
Before you understand the Meta class, you need to master the classes in Python. The notion of a class in python is a bit peculiar, drawing on Smalltalk. In most programming languages, a class is a set of code snippets that describe how to generate an object. This is still true in Python:
Copy Code code as follows:
>>> class Objectcreator (object):
. Pass
...
>>> my_object = Objectcreator ()
>>> Print My_object
<__main__. Objectcreator Object at 0x8974f2c>
However, the classes in Python are much more than that. Class is also an object. Yes, yes, that's the object. As long as you use the keyword Class,python interpreter, an object is created at execution time. The following code snippet:
Copy Code code as follows:
>>> class Objectcreator (object):
. Pass
...
An object is created in memory, and the name is Objectcreator. This object (class) itself has the ability to create objects (class instances), which is why it is a class. But the essence of it is still an object, so you can do the following:
1. You can assign this value to a variable
2. You can copy it.
3. You can add attributes for it
4. You can pass it as a function parameter
Here is an example:
Copy Code code as follows:
>>> Print Objectcreator # You can print a class because it's actually an object
<class ' __main__. Objectcreator ' >
>>> def Echo (o):
... print o
...
>>> Echo (objectcreator) # You can pass a class as a parameter to a function
<class ' __main__. Objectcreator ' >
>>> Print hasattr (objectcreator, ' New_attribute ')
Fasle
>>> objectcreator.new_attribute = ' foo ' # you can add attributes to a class
>>> Print hasattr (objectcreator, ' New_attribute ')
True
>>> Print Objectcreator.new_attribute
Foo
>>> objectcreatormirror = objectcreator # You can assign a class to a variable
>>> Print Objectcreatormirror ()
<__main__. Objectcreator Object at 0x8997b4c>
To create a class dynamically
Because classes are also objects, you can create them dynamically at run time, just like any other object. First, you can create a class in a function, using the class keyword.
Copy Code code as follows:
>>> def choose_class (name):
... if name = = ' Foo ':
... class Foo (object):
. Pass
... return Foo # Returns a class, not an instance of a class
. else:
... class Bar (object):
. Pass
... return Bar
...
>>> MyClass = choose_class (' foo ')
The >>> print MyClass # function returns a class, not an instance of a class
<class ' __main__ '. Foo>
>>> Print MyClass () # You can create a class instance from this class, which is the object
<__main__. Foo Object at 0x89c6d4c>
But it's not that dynamic, because you still need to write your own code for the entire class. Because classes are also objects, they must be generated by something. When you use the class keyword, the Python interpreter automatically creates the object. But like most things in Python, Python still offers you a way to handle it manually. Do you remember the built-in function type? This ancient but powerful function lets you know what the type of object is, like this:
Copy Code code as follows:
>>> print type (1)
<type ' int ' >
>>> Print Type ("1")
<type ' str ' >
>>> print type (objectcreator)
<type ' type ' >
>>> Print type (Objectcreator ())
<class ' __main__. Objectcreator ' >
Here, type has a completely different ability, and it can also dynamically create classes. Type can accept a description of a class as an argument, and then return a class. (I know it's silly to have two different uses of the same function, depending on the parameters passed in, but this is in Python to keep backwards compatibility)
Type can work like this:
Copy Code code as follows:
Type (the class name, the tuple of the parent class (which can be empty for inheritance), and the dictionary (name and value) that contains the attribute.
For example, the following code:
Copy Code code as follows:
>>> class Myshinyclass (object):
. Pass
You can create this manually like this:
Copy Code code as follows:
>>> Myshinyclass = Type (' Myshinyclass ', (), {}) # Returns a Class object
>>> Print Myshinyclass
<class ' __main__. Myshinyclass ' >
>>> Print Myshinyclass () # Create an instance of the class
<__main__. Myshinyclass Object at 0x8997cec>
You will find that we use "Myshinyclass" as the class name, and we can also refer to it as a variable as a reference to a class. Classes and variables are different, and there's no reason to complicate things.
Type accepts a dictionary to define properties for the class, so
Copy Code code as follows:
>>> class Foo (object):
... bar = True
Can be translated as:
Copy Code code as follows:
>>> foo = type (' foo ', (), {' Bar ': True})
And you can use Foo as an ordinary class:
Copy Code code as follows:
>>> Print Foo
<class ' __main__. Foo ' >
>>> Print Foo.bar
True
>>> f = Foo ()
>>> Print F
<__main__. Foo Object at 0x8a9b84c>
>>> Print F.bar
True
Of course, you can inherit from this class, so, like the following code:
Copy Code code as follows:
>>> class Foochild (Foo):
. Pass
Can be written as:
Copy Code code as follows:
>>> foochild = Type (' Foochild ', (Foo,), {})
>>> Print Foochild
<class ' __main__. Foochild ' >
>>> Print Foochild.bar # Bar property is inherited by Foo
True
Eventually you will want to add methods for your class. You just need to define a function with the proper signature and assign it as a property.
Copy Code code as follows:
>>> def echo_bar (self):
... print Self.bar
...
>>> foochild = Type (' Foochild ', (Foo,), {' Echo_bar ': Echo_bar})
>>> hasattr (Foo, ' Echo_bar ')
False
>>> hasattr (foochild, ' Echo_bar ')
True
>>> My_foo = Foochild ()
>>> My_foo.echo_bar ()
True
As you can see, in Python, classes are also objects, and you can create classes dynamically. This is what Python does behind the scenes when you use the keyword class, and this is done through the Meta class.
What exactly is a meta class
The Meta class is the "thing" that is used to create the class. You create a class just to create an instance object for the class, don't you? But we've learned that classes in Python are also objects. Well, the Meta class is used to create these classes (objects), and the Meta class is the class of the class, which you can read:
Copy Code code as follows:
MyClass = Metaclass ()
MyObject = MyClass ()
You've seen the type that allows you to do something like this:
Copy Code code as follows:
MyClass = Type (' MyClass ', (), {})
This is because the function type is actually a meta class. Type is the meta class that Python uses to create all the classes behind it. Now you want to know why the type will all take lowercase instead of type? Well, I guess it's for the sake of consistency with STR, which is the class used to create string objects, and int is the class used to create integer objects. Type is the class that creates the class object. You can see this by checking the class attribute. All the things in Python, notice, I mean everything--all objects. This includes integers, strings, functions, and classes. They are all objects, and they are all created from a class.
Copy Code code as follows:
>>> Age = 35
>>> age.__class__
<type ' int ' >
>>> name = ' Bob '
>>> name.__class__
<type ' str ' >
>>> def foo (): Pass
>>>foo.__class__
<type ' function ' >
>>> class Bar (object): Pass
>>> B = Bar ()
>>> b.__class__
<class ' __main__. Bar ' >
Now, what is the class attribute for any one class?
Copy Code code as follows:
>>> a.__class__.__class__
<type ' type ' >
>>> age.__class__.__class__
<type ' type ' >
>>> foo.__class__.__class__
<type ' type ' >
>>> b.__class__.__class__
<type ' type ' >
So, the meta class is the thing that creates objects like this. If you like, you can call the Meta class The "class factory" (don't mix with the factory class: D) type is the inner Jianyuan class of Python, of course, you can also create your own meta class.
Metaclass Property
You can add a Metaclass attribute to a class when you write it.
Copy Code code as follows:
Class Foo (object):
__metaclass__ = something ...
[...]
If you do this, Python will use the Meta class to create the class Foo. Be careful, there are some tricks here. You first write class Foo (object), but class object Foo is not yet created in memory. Python will look for the metaclass attribute in the definition of the class, and if found, Python will use it to create the class Foo, and if not, the class will be created with the built-in type. Read the following passage several times. When you write the following code:
Copy Code code as follows:
Python does the following:
Is there metaclass this property in Foo? If so, Python creates a class object named Foo in memory via Metaclass (I'm talking about class objects, please follow my ideas). If Python does not find metaclass, it will continue to look for the Metaclass attribute in bar (parent) and try to do the same thing as before. If Python cannot find metaclass in any parent class, it will look for Metaclass in the module hierarchy and try to do the same. If you still can't find Metaclass,python, you create the class object with the built-in type.
The question now is, what code can you put in the metaclass? The answer is: You can create a class of things. So what can be used to create a class? Type, or anything that uses the type or subclass type.
Custom Meta Class
The primary purpose of the Meta class is to automatically change the class when the class is created. Typically, you do something like this for the API, and you want to create classes that match the current context. Imagine a silly example where you decide that all classes in your module should be capitalized. There are several ways to do this, but one is by setting the Metaclass at the module level. In this way, all of the classes in this module are created by using this meta class, and we just need to tell the Meta class that all the attributes are capitalized.
Luckily, Metaclass can actually be arbitrarily invoked, and it doesn't need to be a formal class (I know that something with ' class ' in some names doesn't need to be a class, and it's very helpful to draw and understand.) So, let's start with a simple function as an example.
Copy Code code as follows:
# The Meta class automatically passes the arguments you normally pass to ' type ' as your own argument.
def upper_attr (Future_class_name, Future_class_parents, future_class_attr):
"Returns a class object that converts a property to uppercase"
# Select all attributes that do not start with ' __ '
Attrs = ((name, value) for name, value of Future_class_attr.items () if not name.startswith (' __ ')
# convert them to uppercase
Uppercase_attr = Dict (Name.upper (), value) for name, value in Attrs)
# use ' type ' to create class objects
return type (Future_class_name, future_class_parents, uppercase_attr)
__metaclass__ = upper_attr # This will affect all classes in this module
Class Foo (object):
# We can also define __metaclass__ only here, so that only works in this class
Bar = ' Bip '
Print hasattr (Foo, ' bar ')
# Output: False
Print hasattr (Foo, ' BAR ')
# Output: True
f = Foo ()
Print F.bar
# output: ' Bip '
Now let's do it again, this time using a real class as a meta class.
Copy Code code as follows:
# Keep in mind that ' type ' is actually a class, just like ' str ' and ' int '
# So, you can inherit from type
Class Upperattrmetaclass (Type):
# __new__ is a special way to be called before __init__
# __new__ is the method used to create objects and return them
# and __init__ is just used to initialize the incoming parameters to the object
# You rarely use __new__ unless you want to be able to control the creation of objects
# Here, the objects created are classes, we want to be able to customize it, so we rewrite __new__ here
# If you want, you can do something in the __init__
# There are some advanced usages that involve rewriting __call__ special methods, but we don't have to
def __new__ (Upperattr_metaclass, Future_class_name, Future_class_parents, future_class_attr):
Attrs = ((name, value) for name, value of Future_class_attr.items () if not name.startswith (' __ ')
Uppercase_attr = Dict (Name.upper (), value) for name, value in Attrs)
return type (Future_class_name, future_class_parents, uppercase_attr)
However, this approach is not actually oop. We call the type directly, and we don't have a new method that rewrites the parent class. Now let's deal with this:
Copy Code code as follows:
Class Upperattrmetaclass (Type):
def __new__ (Upperattr_metaclass, Future_class_name, Future_class_parents, future_class_attr):
Attrs = ((name, value) for name, value of Future_class_attr.items () if not name.startswith (' __ ')
Uppercase_attr = Dict (Name.upper (), value) for name, value in Attrs)
# multiplexing Type.__new__ method
# This is basic OOP programming, no magic #
Return type.__new__ (Upperattr_metaclass, Future_class_name, future_class_parents, uppercase_attr)
You may have noticed that there is an additional parameter upperattr_metaclass, which is nothing special. The first parameter of a class method always represents the current instance, as in the self argument in a normal class method. Of course, for the sake of clarity, I've got a long name here. But like self, all parameters have their traditional names. Therefore, in the real product code a meta class should be like this:
Copy Code code as follows:
Class Upperattrmetaclass (Type):
Def __new__ (CLS, name, bases, DCT):
Attrs = ((name, value) for name, value of Dct.items () if not name.startswith (' __ ')
Uppercase_attr = Dict (Name.upper (), value) for name, value in Attrs)
Return type.__new__ (CLS, name, bases, uppercase_attr)
If you use the Super method, we can also make it clearer, which will ease inheritance (yes, you can have the Meta class, inherit from the tuple, inherit from type)
Copy Code code as follows:
Class Upperattrmetaclass (Type):
Def __new__ (CLS, name, bases, DCT):
Attrs = ((name, value) for name, value of Dct.items () if not name.startswith (' __ ')
Uppercase_attr = Dict (Name.upper (), value) for name, value in Attrs)
Return Super (Upperattrmetaclass, CLS). __new__ (CLS, name, bases, uppercase_attr)
That's it, besides, there's really nothing else to say about the Meta class. The reason for the complexity of using the Meta Class code is not because of the Meta class itself, but because you usually use the Meta class to do obscure things, rely on introspection, control inheritance, and more. Indeed, it is particularly useful to use the meta class for "dark Magic", which can result in complex things. But as far as the Meta class itself is concerned, they are actually very simple:
1. Block class creation
2. Modify Class
3. Returns the Modified class
Why use a Metaclass class instead of a function?
Since Metaclass can accept any callable object, why use a class, because it's obvious that using a class is more complicated? There are several reasons for this:
1. The intention would be clearer. When you read Upperattrmetaclass (type), you know what's going to happen next.
2. You can use OOP to program. The meta class can inherit from the class and overwrite the method of the parent class. The Meta class can even use a meta class.
3. You can organize the code better. When you use the Meta class It's certainly not a simple scenario like the one I mentioned above, and it's usually for the more complex issues. It can be helpful to belongs multiple methods into a class and make your code easier to read.
4. You can use special methods such as new, Init, and call. They can help you with different tasks. Even if you can always get everything out of new, some people still feel more comfortable with init.
5. Wow, this thing's name is Metaclass, definitely not angel, I must be careful!
Why should I use a meta class?
Now back to our big topic, why are you using such an error-prone and obscure feature? Well, in general, you can't use it at all:
The Meta class is the depth of the magic, 99% of users should not have to worry about this. If you want to figure out whether you need to use a meta class, then you don't need it. Those who actually use the Meta class know very well what they need to do, and do not need to explain why they use the Meta class at all. Tim Peters, leader of the--python world.
The primary purpose of the Meta class is to create APIs. A typical example is Django ORM. It allows you to define like this:
Copy Code code as follows:
Class person (models. Model):
Name = models. Charfield (max_length=30)
Age = Models. Integerfield ()
But if you do it like this:
Copy Code code as follows:
Guy = person (name= ' Bob ', age= ' 35 ')
Print Guy.age
This does not return a Integerfield object, but instead returns an int and even extracts the data directly from the database. This is possible because of the models. Model defines the Metaclass and uses some magic to turn the simple person class you just defined into a complex hook to the database. The Django framework will simplify these seemingly complex things by exposing a simple API that uses the Meta class, recreating the code through this API, and doing real work behind the scenes.
Conclusion
First, you know that a class is actually an object that can create an instance of a class. Well, in fact, the class itself is also an instance, and of course, they are instances of the class.
Copy Code code as follows:
>>>class Foo (object): Pass
>>> ID (Foo)
142630324
Everything in Python is an object, which is either an instance of a class or an instance of a class, except for type. Type is actually its own meta class, which is not something you can do in a pure Python environment, by playing some small means at the implementation level. Second, the Meta class is very complex. For very simple classes, you may not want to make changes to the class by using the Meta class. There are two other techniques you can use to modify a class:
Copy Code code as follows:
Monkey Patching
Class Decorators
When you need to dynamically modify the class, 99% of the time you better use the above two techniques. Of course, in 99% of the time you do not need to dynamically modify the class.