Object-Oriented Advanced Programming

use _slots _

For the purpose of restriction, Python allows a special __slots__ variable to be defined when defining a class to limit the attributes that the class instance can add:

class Student(object):
__slots__ = ('name', 'age') # Use a tuple to define the names of attributes that are allowed to bind
Then, let's try:

>>> s = Student() # create new instance
>>> s.name = 'Michael' # bind property 'name'
>>> s.age = 25 # bind property 'age'
>>> s. score = 99 # Bind attribute 'score'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'score'
due to 'score' Put it in __slots__, so the score attribute cannot be bound. Trying to bind the score will get an AttributeError error.

When using __slots__, it should be noted that the attributes defined by __slots__ only work on the current class instance, and have no effect on inherited subclasses:

>>> class GraduateStudent(Student):
... pass
...
>>> g = GraduateStudent()
>>> g.score = 9999
unless __slots__ is also defined in the subclass, so that the subclass instance allows the defined The attribute is its own __slots__ plus the parent class's __slots__.

 Use @property

Python's built-in @propertydecorator is responsible for turning a method into an attribute call:

class Student(object):

    @property def score(self): return self._score @score.setter def score(self, value): if not isinstance(value, int): raise ValueError('score must be an integer!') if value < 0 or value > 100: raise ValueError('score must between 0 ~ 100!') self._score = value 

@propertyThe implementation is more complicated, we first examine how to use it. To turn a getter method into a property, you only need to add @propertyit. At this time, @propertyanother decorator is created @score.setter, which is responsible for turning a setter method into a property assignment, so we have a controllable property operation:

>>> s = Student()
>>> s.score = 60 # OK,实际转化为s.set_score(60)
>>> s.score # OK,实际转化为s.get_score() 60 >>> s.score = 9999 Traceback (most recent call last): ... ValueError: score must between 0 ~ 100! 

Noticing this magic @property, when we operate on instance properties, we know that the properties are probably not directly exposed, but implemented through getter and setter methods.

You can also define read-only properties, only define the getter method, and not define the setter method is a read-only property:

class Student(object):

    @property def birth(self): return self._birth @birth.setter def birth(self, value): self._birth = value @property def age(self): return 2015 - self._birth 

The above birthis a read-write property, but agea read-only property, because it can be calculated agebased on the current time.birth

multiple inheritance

Example:

class Dog(Mammal, RunnableMixIn, CarnivorousMixIn):
    pass

custom class

Pay attention to the variable name or function name of _xxx_, which has a special purpose in python

__str__

__iter__ use class for loop


__getitem__ takes out elements in a class like l takes out elements ist

class Fib(object):
    def __getitem__(self, n):
        a, b = 1, 1
        for x in range(n):
            a, b = b, a + b
        return a

__getattr__

 Dynamically returns a property.

Note that it will only be called if the property is not found __getattr__, existing properties, for example name, will not __getattr__be looked up in.

Also, notice that any invocation s.abcwill return None, because the __getattr__default return we defined is just that None. To make the class respond to only a few specific properties, we have to throw AttributeErroran error according to the convention:

class Student(object):

    def __getattr__(self, attr): if attr=='age': return lambda: 25 raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr) 

This can actually process all the properties and method calls of a class dynamically without any special means.

What does this fully dynamic invocation feature actually do? The effect is that it can be called for a completely dynamic situation.

__call__()

  def __call__ (self):
        print( 'My name is %s.' % self.name)
can call the instance and define parameters.

Use an enumeration class

from enum import Enum

Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))

If you need more precise control over the enumeration type, you can Enumderive a custom class from:

from enum import Enum, unique

@unique
class Weekday(Enum): Sun = 0 # Sun的value被设定为0 Mon = 1 Tue = 2 Wed = 3 Thu = 4 Fri = 5 Sat = 6


使用元类
type()
函数可以查看一个类型或变量的类型,Hello是一个class,它的类型就是type,而h是一个实例,它的类型就是class Hello
metaclass 元类
先定义metaclass,就可以创建类,最后创建实例。换句话说,你可以把类看成是metaclass创建出来的“实例”。

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

# metaclass是类的模板,所以必须从`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 to use ListMetaclass to customize the class when defining the class, and pass in the keyword parameters metaclass:

class MyList(list, metaclass=ListMetaclass):
    pass 

The magic takes effect when we pass in keyword arguments metaclass, it instructs the Python interpreter to create MyListit through ListMetaclass.__new__(), where we can modify the class definition, for example, add a new method, and then return to modify later definition.

__new__()The parameters received by the method are:

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

  2. class name;

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

  4. A collection of methods for the class.

Test MyListto see if the method can be called add():

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

Write the most complicated ModelMetaclass:

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 # 保存属性和列的映射关系 attrs['__table__'] = name # 假设表名和类名一致 return type.__new__(cls, name, bases, attrs) 

and the base class Model:

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 one class User(Model), the Python interpreter first Usersearches in the definition of the current class metaclass. If it is not found, it continues to search in the parent class. If it is found Model, metaclassit uses Modelthe one defined in metaclassit ModelMetaclassto create the Userclass, that is, the metaclass can be hidden. Inherit to subclasses, but subclasses don't feel it themselves.

In ModelMetaclass, a total of several things have been done:

  1. exclude Modelmodifications to the class;

  2. Find all the attributes of the defined class in the current class (for example User), if you find a Field attribute, save it to a __mappings__dict, and delete the Field attribute from the class attribute at the same time, otherwise, it is easy to cause runtime errors (instance The property of the class will overwrite the property of the same name of the class);

  3. Save the table name to __table__, here it is simplified as the table name defaults to the class name.

In the Modelclass, you can define various methods of operating the database, such as save(), delete(), find(), updateand so on.

We implemented the save()method to save an instance to the database. With table names, attribute-to-field mappings, and collections of attribute values, INSERTstatements can be constructed.

Try writing code:

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

The output is as follows:

Found model: User
Found mapping: email ==> <StringField:email>
Found mapping: password ==> <StringField:password> Found mapping: id ==> <IntegerField:uid> Found mapping: name ==> <StringField:username> SQL: insert into User (password,email,username,id) values (?,?,?,?) ARGS: ['my-pwd', '[email protected]', 'Michael', 12345] 

As you can see, the save()method has printed out the executable SQL statement and the parameter list. You only need to actually connect to the database and execute the SQL statement to complete the real function.

Guess you like

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