It __slots__
__xxx__
is important to note that these are similar to variables or function names, which have special uses in Python.
__slots__
We already know how to use it, and __len__()
we know it so that we can get class to len()
function.
In addition, there are many special purpose functions in Python's class that can help us customize the class.
__str__
Let's first define a Student
class and print an instance:
>>> class Student(object):... def __init__(self, name):... self.name = name...>>> print Student(‘Michael‘)<__main__.Student object at 0x109afb190>
Print out a bunch <__main__.Student object at 0x109afb190>
, not pretty.
How can you print it well? Just define a good __str__()
method and return a nice-looking 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)
This kind of printed example, not only good-looking, but also easy to see the important data inside the instance.
But careful friends will find that the direct knocking variable print
is not used, the printed example is not good to see:
>>> s = Student(‘Michael‘)>>> s<__main__.Student object at 0x109afb310>
This is because the direct display of variable calls is not __str__()
, but the __repr__()
difference between the two is to return the string that the __str__()
user sees, and the __repr__()
string that the program developer sees, that __repr__()
is, for debugging services.
The solution is to define one more __repr__()
. But it's usually __str__()
__repr__()
the same as the code, so there's a lazy way to do it:
class Student(object): def __init__(self, name): self.name = name def __str__(self): return ‘Student object (name=%s)‘ % self.name __repr__ = __str__
__iter__
If a class wants to be used for for ... in
loops, like a list or a tuple, it must implement a __iter__()
method that returns an iterative object, and then the python for loop constantly calls the iteration object's next()
method to get the next value of the loop. Exits the loop until a stopiteration error is encountered.
For the Fibonacci sequence, we write a fib class that can be used for a For loop:
ClassHidef __init__< Span class= "params" (self): SELF.A, self.b = 0, 1 # Initialize two counters, a, b def __iter__ ( Self): return "
# "function"
def next (self): SELF.A, self.b = self.b, SELF.A + self.b # calculates the next value if self.a > 100000: # exit the Loop condition raise stopiteration (); return self.a # return next value
Now, try acting on the For loop with the FIB instance:
>>> for n in Fib():... print n...11235...4636875025
__getitem__
Although the FIB instance can be used for a for loop, it looks a bit like the list, but using it as a list or not, for example, take the 5th element:
>>> Fib()[5]Traceback (most recent call last): File "<stdin>", line 1, in <module>TypeError: ‘Fib‘ object does not support indexing
To act as a list to take out an element according to the subscript, you need to implement the __getitem__()
method:
class Fib(object): def __getitem__(self, n): a, b = 1, 1 for x in range(n): a, b = b, a + b return a
Now, you can click on any of the numbers to access the sequence:
>>> f = Fib()>>> f[0]1>>> f[1]1>>> f[2]2>>> f[3]3>>> f[10]89>>> f[100]573147844013817084101
But the list has a magical slicing method:
range(100)[5:10][5, 6, 7, 8, 9]
The
has an error for the FIB. The reason is that __getitem__ ()
passed in an argument that could be an int or a slice object slice
, so make a decision:
class Fib(object): def __getitem__(self, n): if isinstance(n, int): a, b = 1, 1 for x in range(n): a, b = b, a + b return a if isinstance(n, slice): start = n.start stop = n.stop a, b = 1, 1 L = [] for x in range(stop): if x >= start: L.append(a) a, b = b, a + b return L
Now try the fib slices:
>>> f = Fib()>>> f[0:5][1, 1, 2, 3, 5]>>> f[:10][1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
However, the step parameter is not processed:
f[:10:2][1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
There is no processing of negative numbers, so there __getitem__()
is still a lot of work to be done to properly implement one.
In addition, if the object dict
is considered, __getitem__()
the argument may be an object that can be a key, for example str
.
The corresponding method is to __setitem__()
assign a value to the set as a list or a Dict object. Finally, there is a __delitem__()
way to delete an element.
In short, by the above method, our own definition of the class and Python comes with the list, tuple, dict no difference, this is due to the dynamic language "duck type", do not need to force inheritance of an interface.
__getattr__
Normally, when we call a method or property of a class, it will be an error if it does not exist. For example Student
, define a class:
class Student(object): def __init__(self): self.name = ‘Michael‘
Call name
Properties, no problem, but call a nonexistent score
property, there is a problem:
>>> s = Student()>>> print s.nameMichael>>> print s.scoreTraceback (most recent call last): ...AttributeError: ‘Student‘ object has no attribute ‘score‘
The error message clearly tells us that the attribute was not found score
.
To avoid this error, in addition to adding a score
property, Python has another mechanism, which is to write a __getattr__()
method that dynamically returns an attribute. Modify the following:
class Student(object): def __init__(self): self.name = ‘Michael‘ def __getattr__(self, attr): if attr==‘score‘: return 99
When a non-existent property is called, for example score
, the Python interpreter attempts to invoke __getattr__(self, ‘score‘)
the property, so that we have a chance to return score
the value:
>>> s = Student()>>> s.name‘Michael‘>>> s.score99
The return function is also perfectly possible:
class Student(object): def __getattr__(self, attr): if attr==‘age‘: return lambda: 25
Just call the method to change to:
>>> s.age()25
Note that an existing attribute is called only if no attribute is found, __getattr__
for example name
, it is not located in __getattr__
.
Also, notice that any call s.abc
will return None
, because the default return that we define is __getattr__
None
. To allow class to respond to only a few specific properties, we are going to throw the error according to the Convention AttributeError
:
class Student(object): def __getattr__(self, attr): if attr==‘age‘: return lambda: 25 raise AttributeError(‘\‘Student\‘ object has no attribute \‘%s\‘‘ % attr)
This actually allows all the properties and method invocations of a class to be dynamically processed, without any special means.
What is the actual function of this fully dynamic invocation? The effect is that it can be called for a completely dynamic situation.
As an example:
Now a lot of websites are doing rest API, such as Sina Weibo, watercress What, call API URL similar:
http://api.server/user/friendshttp://api.server/user/timeline/list
If you want to write the SDK, to each URL corresponding to the API to write a method, it is exhausting, and, once the API changes, the SDK will be changed.
With completely 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
Try:
>>> Chain().status.user.timeline.list‘/status/user/timeline/list‘
In this way, regardless of the API changes, the SDK can be based on the URL to achieve a full dynamic call, and not with the increase of the API to change!
There are rest APIs that put parameters in URLs, such as GitHub's API:
GET /users/:user/repos
When calling, you need to :user
replace the actual user name. If we can write a chain call like this:
Chain().users(‘michael‘).repos
It is very convenient to invoke the API. Interested children's shoes can try to write them out.
__call__
An object instance can have its own properties and methods, which we use to invoke when we invoke an instance method instance.method()
. Can you call it directly on the instance itself? Similar instance()
? In Python, the answer is yes.
In any class, you can simply __call__()
invoke the instance by defining a method. Take a 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 method is called as follows:
>>> s = Student(‘Michael‘)>>> s()My name is Michael.
__call__()
You can also define parameters. Making a direct call to an instance is like calling a function, so you can think of the object as a function and the function as an object because there is no fundamental difference between the two.
If you look at the object as a function, then the function itself can be created dynamically at runtime, because instances of the class are created at run time, so we blur the bounds of the object and the function.
So, how do you tell if a variable is an object or a function? In fact, more often, we need to determine whether an object can be called, the object that can be called is an Callable
object, such as the function and the class instance we have defined above __call()__
:
>>> callable(Student())True>>> callable(max)True>>> callable([1, 2, 3])False>>> callable(None)False>>> callable(‘string‘)False
Through callable()
the function, we can determine whether an object is a callable object.
Reference Link: https://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/ 0013946328809098c1be08a2c7e4319bd60269f62be04fa000
Python's Custom class