Python study notes 12 - metaclasses

# The type() function can be used both to view the type of an object, and to create new types, such as
def hello(self, name='world'):
    print('hello %s' % name)


Hello = type('Hello', (object,), dict(hello=hello))  # 创建Hello类
h = Hello()
h.hello()


# Equivalent to
class Hello1(object):
    def hello(self, name='world'):
        print('Hello %s' % name)


h1 = Hello1()
h1.hello()
print(type(Hello) == type(Hello1))
print(type(h) == type(h1))
''' To create a class through type(), the type() function passes in 3 parameters in turn:
        1. The name of the class
        2. Integrated parent class collection (using the array tuple writing method, if there is only one parent class, you need to use the single element writing method of tuple)
        3. The method name of the class is bound to the function
    A class created by type() is exactly the same as writing a class directly, because the Python interpreter also creates a class by calling the type() function when it encounters a class definition.
    Under normal circumstances, we define classes in the way of class xxx, but dynamic languages ​​themselves also support dynamic creation of classes through the type() function at runtime.
'''


# metaclass (metaclass), the relationship between metaclass and class and instance: first define metaclass, create class according to metaclass, and then create an instance of the class, which is equivalent to the "instance" created by metaclass
# Similar to the metaclass is the template of the class, the class is the template of the instance, their creation is based on the template
# By default, the class name of meatclass is always defined with Metaclass at the end to make it clear that this is a metaclass
class ListMetaclass(type): # metaclass is a template for a class, so it must be derived from the 'type' 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 to use ListMetaclass to customize the class when defining the class, and pass in the keyword parameter metaclass:
class MyList(list, metaclass=ListMetaclass):
    pass


# When we pass in the keyword argument metaclass, it instructs the Python interpreter to create MyList via ListMetaclass.__new__()
# The parameters received by the __new__() method are: the object of the class currently to be created; the name of the class; the set of parent classes inherited by the class; the set of methods of the class
# Test the add() method of MyList
L = MyList()
L.add(1)
print(L)


# output: [1]

# metaclass is generally used in ORM framework (ORM full name Object Relational Mapping, that is, object-relational mapping, is to map a row of relational database to an object
# That is, a class corresponds to a table, so it is easier to write code, and you don't need to directly manipulate SQL statements. To write an ORM framework, all classes can only be dynamically defined, because only the use of
# The user can define the corresponding class according to the structure of the table)
# First define the Field class to save the field name and field type of the database table
class Field(object):
    def __init__(self, name, column_type):
        self.name = name
        self.column_type = column_type

    def __str__(self):
        return '%s:%s' % (self.name, self.column_type)


# Further define various types of Field on the basis of Field
class StringField(Field):
    def __init__(self, name):
        super(StringField, self).__init__(name, 'vachar(100)')


class IntegerField(Field):
    def __init__(self, name):
        super(IntegerField, self).__init__(name, 'bigint')


class ModelMetaclass(type):
    def __new__(cls, name, bases, attrs):
        if name == 'Model':
            return type.__new__(cls, name, bases, attrs)
        print("Found model:%s" % name)
        mappings = dict()
        for k, v in attrs.items():
            if isinstance(v, Field):
                print('Found mapping:%s => %s' % (k, v))
                mappings [k] = v
        for k in mappings.keys():
            attrs.pop(k)
        attrs['__mappings__'] = mappings # Save the mapping between attributes and columns
        attrs['__table__'] = name # Assume the table name is the same as the class name
        return type.__new__(cls, name, bases, attrs)


class Model(dict, metaclass=ModelMetaclass):
    def __init__(self, **kw):
        super(Model, self).__init__(**kw)

    def __getattr__(self, key):
        try:
            return self[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 = []
        for k, v in self.__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 a User(Model), the Python interpreter first looks for the metaclass in the definition of the current class User, if not, it looks up the metaclass in the parent class Model,
# After finding it, use the metaclass defined in the Model to create the User class, that is, the metaclass can be implicitly inherited to the subclass
# Do the following things in ModelMetaclass:
# 1. Exclude modifications to the Model class
# 2. Find all attributes of the defined class in the current class (such as User). If you find a Field attribute, save it to a dict of __mappings__, and colleagues delete the Field attribute from the class attribute
# 3. Save the table name to __table__, which is simplified to use the class name as the default table name
# In the Model class, you can define various methods of operating the database, such as save(), delete(), find(), update(), etc.
class User(Model):
    id = IntegerField('id')
    name = StringField('username')
    email = StringField('email')
    password = StringField('password')


# create an instance
u = User(id=123, name='Michael', email='[email protected]', password='pw')
u.save()

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324777780&siteId=291194637