python——__slots__/@property/__str__/__repr__() __iter__和__next__ __getitem__ __getattr__ __call_

table of Contents

__slots__

Use @property

__str__ and __repr__()

__iter__ and __next__

__getitem__

__getattr__

 __call__


__slots__

不是在class中创建方法而是创建了一个链接把外部的set_age 方法用链接指到Student内

__slots__: In fact, it is to lock the name in the class, instantiate the object, can only assign and call, not delete attributes and add new attributes

__slots__What is it: It is a class variable, the variable value can be a list, a primitive ancestor, or an iterable object, or a string (that means that all instances have only one data attribute)

Public section:

from types import MethodType 
#创建一个方法
def set_age(self, arg):
    self.age = arg    
#创建一个类    
class Student(object):
    pass

Bind an attribute to the instance :

>>> s = Student()
>>> s.name = 'Michael' # 动态给实例绑定一个属性
>>> print(s.name)
Michael

Bind a method to the instance :

>>> from types import MethodType
>>> s.set_age = MethodType(set_age, s) # 给实例绑定一个方法
>>> s.set_age(25) # 调用实例方法
>>> s.age # 测试结果
25

The method bound to one instance does not work for another instance

In order to bind methods to all instances, you can bind methods to class:

Student.set_score = set_score
Student.set_age = MethodType(set_age,Student)

If we want to limit the attributes of the instance, when defining the class, define a special __slots__variable to limit the attributes that the class instance can add :

class Student(object):
    __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
>>> s = Student() # 创建新的实例
>>> s.name = 'Michael' # 绑定属性'name'
>>> s.age = 25 # 绑定属性'age'
>>> s.score = 99 # 绑定属性'score'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'score'

__slots__The defined attributes only work on the current class instance, and have no effect on inherited subclasses:

Unless it is also defined in the subclass __slots__, in this way, the attributes that the subclass instance allows to define are its own __slots__plus the parent class __slots__.

 

Use @property

In order to limit the scope of the score, one set_score()method can be used to set the score, and then one get_score()can be used to obtain the score. In this way, in the set_score()method, you can check the parameters:

class Student(object):
    def get_score(self):
        return self._score
    def set_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

The above calling method is a bit more complicated, it is not as straightforward as using attributes directly. For class methods, decorators also work. 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
#使用
>>> 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!

The essence of a static property property is to implement the three methods of get, set, and delete

Syntactic sugar can be used to set and delete similar properties of property, which is no different from general property setting and deletion

To turn a getter method into a property, you only need to add @propertyit. At this time, another decorator is@property created by itself , which is responsible for turning a setter method into a property assignment, so we have a controllable property operation@score.setter

You can also define a read-only property, and only define a getter method. If you don't define a setter method, it 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 2014 - self._birth

The above birthis a readable and writable attribute, but agea read-only attribute

 

__str__ and __repr__()

__str__Is the string display that controls the change object

At the print(obj)/'%s'%obj/str(obj)time, the 了obj.__str__method is actually called internally . If the str method has, then it must return a string. If there is no __str__method, the method in this class will be searched first __repr__, and the parent class (object) will not be searched again__str__ .

Define a Studentclass and print an instance:

>>> class Student(object):
...     def __init__(self, name):
...         self.name = name
...
>>> print(Student('Michael'))
<__main__.Student object at 0x109afb190>

It prints out a bunch <__main__.Student object at 0x109afb190>and it doesn't look good.

How can I print it nicely? Just define the __str__()method and return a nice string:

>>> class Student(object):
...     def __init__(self, name):
...         self.name = name
...     def __str__(self):
...         return 'Student object (name: %s)' % self.name
...
>>> print(Student('Michael'))
Student object (name: Michael)

 However, typing directly on the variable and printing out without print is not pretty, because the variable call is displayed directly __repr__(). The difference between the two is __str__()that the string that the user sees is returned, and the string that the __repr__()program developer sees is returned, that is, it __repr__()is Debugging service.

Can be added to the code

 __repr__ = __str__

__repr__It is also the way to display the name of the object, using repr(object) or "%r"%object will call the __repr__method

repr is the spare tire of str, but str cannot be the spare tire of repr

class Foo:
​
    def __str__(self):
        return "控制打印对象时的显示形式"
​
    def __repr__(self):
        print("在执行repr")
        return "repr Foo"
    pass
f = Foo()
print("%r"%f)
print(f)

#这里print("%r"%f) 调用重新定义的__repr__方法,print(f)调用__str__方法
class Foo:
​
    def __str__(self):
        return "控制打印对象时的显示形式"
​
    # def __repr__(self):
    #     print("在执行repr")
    #     return "repr Foo"
    pass
f = Foo()
print(repr(f)) #输出结果 <__main__.Foo object at 0x002EB550>

In the above code, the __repr__method needs to be called . This class does not have it. The __repr__method of the parent class is directly borrowed without executing the __str__method. So repr is the spare tire of str, but str cannot be the spare tire of repr

__iter__ and __next__

If you want to be a class for for ... inthe cycle, similar to that list or tuple, you must implement a __iter__()method , the method returns an iterator object, and then, Python for loop iteration will continue to call the object's __next__()method to get the next cycle of value Until StopIterationit exits the loop when an error is encountered

Taking the Fibonacci sequence as an example, write a Fib class that can be used in a for loop:

class Fib(object):
    def __init__(self):
        self.a, self.b = 0, 1 # 初始化两个计数器a,b

    def __iter__(self):
        return self # 实例本身就是迭代对象,故返回自己

    def __next__(self):
        self.a, self.b = self.b, self.a + self.b # 计算下一个值
        if self.a > 100000: # 退出循环的条件
            raise StopIteration()
        return self.a # 返回下一个值
#使用
>>> for n in Fib():
...     print(n)
1
1
2
3
5
...
46368
75025
class Range:
    def __init__(self, n, stop, step):
        self.n = n
        self.stop = stop
        self.step = step

    def __next__(self):
        if self.n >= self.stop:
            raise StopIteration
        x = self.n
        self.n += self.step
        return x

    def __iter__(self):
        return self


for i in Range(1, 7, 3):
    print(i)

__getitem__

Although the Fib instance can be used in a for loop, it still cannot be used as a list. For example, take the fifth element:

>>> Fib()[5]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'Fib' object does not support indexing

To behave like a list to extract elements according to the subscript , you need to implement __getitem__()methods:

class Fib(object):
    def __getitem__(self, n):
        a, b = 1, 1
        for x in range(n):
            a, b = b, a + b
        return a
#使用
>>> f = Fib()
>>> f[0]
1

But the slicing method of the list: an error is reported for Fib . The reason is __getitem__()that the parameter passed in may be an int or a slice object slice, so you have to judge:

class Fib(object):
    def __getitem__(self, n):
        if isinstance(n, int): # n是索引
            a, b = 1, 1
            for x in range(n):
                a, b = b, a + b
            return a
        if isinstance(n, slice): # n是切片
            start = n.start
            stop = n.stop
            if start is None:
                start = 0
            a, b = 1, 1
            L = []
            for x in range(stop):
                if x >= start:
                    L.append(a)
                a, b = b, a + b
            return L
#使用
>>> f = Fib()
>>> f[0:5]
[1, 1, 2, 3, 5]

 In addition, if you regard the object as an object dict, __getitem__()the parameter may also be an object that can be used as a key. For example str,

Corresponding to it is the __setitem__()method, which treats the object as a list or dict to assign a value to a collection. Finally, there is a __delitem__()method for deleting an element.

In short, through the above method, the class defined by ourselves is no different from the list, tuple, and dict that comes with Python.

__getattr__

When we call a method or attribute of a class, if it does not exist, an error will be reported. To avoid this error, in addition to adding an scoreattribute, it is to write a __getattr__()method that dynamically returns an attribute, and the return function is also completely possible:

class Student(object):

    def __init__(self):
        self.name = 'Michael'

    def __getattr__(self, attr):
        if attr=='score':
            return 99

 Note that it is only called if the attribute is not found__getattr__

Note that any call such as s.abcwill return None, this is because the __getattr__default return we defined is None. To make the class respond to only a few specific attributes, we have to follow the convention and throw AttributeErroran error:

class Student(object):

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

If you want to write an SDK and write a method to the API corresponding to each URL, it will be exhausting. Moreover, once the API is changed, the SDK must be changed.

Using fully dynamic __getattr__, we can write a chain call:

class Chain(object):

    def __init__(self, path=''):
        self._path = path

    def __getattr__(self, path):
        return Chain('%s/%s' % (self._path, path))

    def __str__(self):
        return self._path

    __repr__ = __str__
>>> Chain().status.user.timeline.list
'/status/user/timeline/list'

There are also some REST APIs that put parameters in the URL, such as GitHub's API:

GET /users/:user/repos

When calling, you need to :userreplace it with the actual user name. If we can write a chain call like this:

Chain().users('michael').repos

 __call__

For any class, you only need to define a __call__()method to directly call the instance. Look at the example:

class Student(object):
    def __init__(self, name):
        self.name = name

    def __call__(self):
        print('My name is %s.' % self.name)

The calling method is as follows:

>>> s = Student('Michael')
>>> s() # self参数不要传入
My name is Michael.

__call__()You can also define parameters. Calling an instance directly is like calling a function

Through callable()functions, we can determine whether an object is a "callable" object, such as functions and __call__()the class instances we defined above :

>>> callable(Student())
True
>>> callable(max)
True
>>> callable([1, 2, 3])
False
>>> callable(None)
False
>>> callable('str')
False

Guess you like

Origin blog.csdn.net/zangba9624/article/details/106249735