27 Using meta-classes

Source: Internet
Author: User
Tags define function

Type ()

The biggest difference between dynamic and static languages is the definition of functions and classes, not defined at compile time, but dynamically created at run time.

For example, if we want to define a Hello class, we write a hello.py module:

class Hello (object):    def hello (self, name='World'):        print (  'Hello,%s. ' % name ')

When the Python interpreter loads the hello module, it executes all the statements of that module sequentially, and the result is a dynamically created Hello class object, which is tested as follows:

 from Hello import Hello>>> h = Hello ()>>> H.hello () Hello, world. >>> Print (Type (Hello))<class'type'>>>>  Print (Type (h))<class'hello. Hello'>

type()A function can see the type of a type or variable, it Hello is a class, its type is type , and it h is an instance, and its type is class Hello .

We say that the definition of class is created dynamically at runtime, and the method of creating class is to use type() functions.

type()A function can either return the type of an object, or create a new type, for example, we can type() create a class from a function without Hello having to pass class Hello(object)... the definition:

>>> def fn (self, name=' World'): # define function First ... print ('Hello,%s.'%name) ...>>> Hello = Type ('Hello', (Object,), Dict (HELLO=FN) # Create Helloclass>>> h =Hello ()>>>H.hello () Hello, world.>>>print (Type (Hello))<class 'type'>>>>Print (Type (h))<class '__main__. Hello'>

To create a class object, the type() function passes through 3 parameters in turn:

    1. The name of the class;
    2. Inherited parent class Collection, note that Python supports multiple inheritance, if there is only one parent class, do not forget the single element of tuple notation;
    3. Class's method name and function binding, where we bind the function fn to the method name hello .

The type() class created by the function is exactly the same as the direct write class, because when the Python interpreter encounters the class definition, it simply scans the syntax of the class definition and then invokes type() the function to create the class.

Normally, we use class Xxx... to define the class, but the type() function also allows us to dynamically create the class, that is, the dynamic language itself supports the runtime dynamic creation of classes, which is very different from static language, to create a class in the static language runtime, you must construct the source code string and then call the compiler, Or the use of some tools to generate bytecode implementations, essentially dynamic compilation, can be very complex.

Metaclass

In addition type() to using dynamically created classes, you can also use Metaclass to control the creation behavior of classes.

Metaclass, literal translation is a meta-class, the simple explanation is:

Once we have defined the class, we can create an instance from this class, so: Define the class first, and then create the instance.

But what if we want to create a class? Then you must create the class from Metaclass, so define the Metaclass first, and then create the class.

The connection is: Define Metaclass First, you can create the class, and finally create the instance.

So, Metaclass allows you to create classes or modify classes. In other words, you can think of a class as an "instance" created by Metaclass.

Metaclass is the most difficult and difficult-to-use magic code for Python-oriented objects. Under normal circumstances, you will not encounter the need to use metaclass situation, so, the following can not understand it is OK, because basically you will not use.

Let's look at a simple example where this metaclass can add a method to our custom MyList add :

Definition ListMetaclass , according to the default habit, the class name of Metaclass always ends with metaclass so that it is clear that this is a metaclass:

# Metaclass is a template for a class, so you must derive from the ' type ' type: class Listmetaclass (type):    def __new__ (CLS, name, bases, attrs):        attrs['add' ] = Lambda self, value:self.append (value)        return type.__new__ (CLS, name, bases, Attrs)

With Listmetaclass, we also instruct the class to use Listmetaclass to customize the class, passing in the keyword parameters metaclass :

class MyList (list, metaclass=listmetaclass):    Pass

When we pass in the keyword argument metaclass , the magic takes effect, and it instructs the Python interpreter to create it when it is created, where MyList ListMetaclass.__new__() we can modify the definition of the class, for example, by adding a new method, and then returning the modified definition.

__new__()The parameters that are received by the method are:

    1. The object of the class that is currently ready to be created;

    2. The name of the class;

    3. The collection of parent classes that the class inherits;

    4. The collection of methods for the class.

Test MyList Whether the method can be called add() :

>>> L = MyList ()>>> l.add (1)>> l[1]

listthere is no ordinary add() way:

>>> L2 = list ()>>> l2.add (1) Traceback (most recent call last):  " /c4><stdin>"1 in <module>"list ' Object ' Add '

What is the meaning of dynamic modification? MyListisn't it easier to write the method directly in the definition add() ? Under normal circumstances, it should be written directly, through metaclass modification is purely perverted.

However, you will always encounter the need to modify the class definition through Metaclass. ORM is a typical example.

ORM Full Name "Object Relational Mapping", that is, object-relational mapping, is to map a row of the relational database to an object, that is, a class corresponding to a table, so that the code is simpler to write, do not directly manipulate the SQL statement.

To write an ORM framework, all classes can only be defined dynamically, because only the user can define the corresponding class based on the structure of the table.

Let's try to write an ORM framework.

The first step in writing the underlying module is to write the calling interface first. For example, if the user uses this ORM framework and wants to define a User class to manipulate the corresponding database table User , we expect him to write this code:

classUser (Model): # Defines a class's attribute-to-column mapping: ID= Integerfield ('ID') name= Stringfield ('username') Email= Stringfield ('Email') Password= Stringfield ('Password') # Create an instance: U= User (id=12345, name='Michael', email='[email protected]', password='my-pwd') # Save to Database: U.save ()

Among them, the parent class Model and the attribute type StringField , IntegerField are provided by the ORM Framework, and the remaining magic methods such as save() all are automatically completed by Metaclass. Although Metaclass is more complex to write, the user of ORM is surprisingly simple to use.

Now, let's implement the ORM by pressing the interface above.

First, define the Field class, which is responsible for saving the field name and field type of the database table:

class Field (object):    def __init__ (self, Name, Column_type):        = name        = column_ Type    def __str__ (self):        return'<%s:%s>' % (self.__ class__.__name__, Self.name)

On Field the basis of the further definition of various types Field , such as StringField , and IntegerField so on:

class Stringfield (Field):    def __init__ (self, name):        'varchar'  class  Integerfield (Field):    def __init__ (self, name):        'bigint  ')

The next step is to write the most complex ModelMetaclass :

classModelmetaclass (type): Def __new__ (CLS, name, bases, attrs):ifname=='Model':            returntype.__new__ (CLS, name, bases, attrs) print ('Found Model:%s'%name) Mappings=dict () forKvinchAttrs.items ():ifisinstance (V, Field): Print ('Found Mapping:%s ==>%s'%(k, V)) Mappings[k]=v forKinchMappings.keys (): Attrs.pop (k) attrs['__mappings__'] =Mappings # Save mappings for properties and columns attrs['__table__'] =Name # assumes that the table name and class name matchreturntype.__new__ (CLS, name, bases, Attrs)

and the base class Model :

classModel (Dict, metaclass=modelmetaclass): Def __init__ (self,**kw): Super (Model, self). __init__ (**kw) def __getattr__ (self, key):Try:            returnSelf[key] except keyerror:raise attributeerror (R"' Model ' object has no attribute '%s '"%key) def __setattr__ (self, Key, value): Self[key]=Value def save (self): fields= []        params=[] args= []         forKvinchSelf.__mappings__.items (): Fields.Append (v.name)params. Append ('?') Args.append (GetAttr (self, k, None)) SQL='insert into%s (%s) values (%s)'% (self.__table__,','. Join (Fields),','. Join (params)) Print ('SQL:%s'%sql) Print ('ARGS:%s'% str (args))

When the user defines one class User(Model) , the Python interpreter first looks in the definition of the current class User , and metaclass if it is not found, continues to look in the parent class, Model finds it metaclass , uses Model metaclass ModelMetaclass the To create a User class, that is, Metaclass can be implicitly inherited to subclasses, but the subclass does not feel it.

In ModelMetaclass , there are a few things that have been done:

    1. Eliminate the modification of the Model class;

    2. Finds all the properties of the defined class in the current class (for example User ), if a field property is found, it is saved to a __mappings__ dict, and the field property is removed from the class attribute, otherwise it is prone to run-time errors (the attribute of the instance obscures the same name property of the Class);

    3. Save the table name to the table name __table__ , which is simplified to the class name by default.

In a Model class, you can define various methods of manipulating the database, such as,,, and save() so on delete() find() update .

We implemented the save() method to save an instance to the database. A statement can be constructed because of a table name, a property-to-field mapping, and a collection of property values INSERT .

To write code try:

U = User (id=12345, name='Michael', email='[email protected] ', password='my-pwd') u.save ()

The output is as follows:

 found model:userfound mapping:email  ==> <stringfield:email>< Span style= "color: #000000;" >found mapping:password  ==> <stringfield:password>found Mapping:id 
    ==> <integerfield:uid>found mapping:name  ==> <stringfield : Username>sql:insert into User (password,email,username,id) VALUES (?,?,?,?    , "    ", "  michael  , 12345 ] 

As you can see, the save() method has printed out the executable SQL statement, as well as the parameter list, just to really connect to the database, execute the SQL statement, you can complete the real function.

With less than 100 lines of code, we implemented a streamlined ORM framework through Metaclass.

Summary

Metaclass is a very magical object in Python that can change the behavior of a class when it is created. This powerful feature must be used with caution.

27 Using meta-classes

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.