__init__
or __lt__
). Also, they are not as well documented as we would like. All magic methods are described in the documentation, but very randomly and almost without any organization. Therefore, in order to correct what I perceive as a lack of Python documentation, I am going to provide more information about magical methods, written in plain language and richly supplied with examples. I hope you enjoy this guide. Use it as a teaching material, memo, or full description. I just tried to describe magical methods as clearly as possible. __init__
. With it, we can initialize the object. However, when I write x = SomeClass()
, __init__
not the first thing that is called. In fact, an object instance creates a __new__
method, and then the arguments are passed to the initializer. At the other end of the object's life cycle is the __del__
method. Let's take a closer look at these three magic methods:__new__(cls, [...)
__init__
. __new__
used very rarely, but is sometimes useful, in particular, when a class is inherited from an immutable type, such as a tuple or string. I do not intend to dwell on __new__
in __new__
, since it is not something that is often needed, but this method is described very well and in detail in the documentation .__init__(self, [...)
x = SomeClass(10, 'foo')
, __init__
gets 10
and 'foo'
as arguments. __init__
almost universally used when defining classes.__del__(self)
__new__
and __init__
form an object constructor, __del__
is its destructor. It does not define the behavior for the expression del x
(therefore this code is not equivalent to x.__del__()
). Rather, it defines the behavior of the object at the time the object enters the garbage collector. This can be quite convenient for objects that may require additional clean-ups during deletion, such as sockets or file objects. However, you need to be careful, since there is no guarantee that __del__
will be called if the object continues to live when the interpreter shuts down. Therefore, __del__
cannot serve as a substitute for good programmer practices (always terminate the connection, if you have finished working with it, and the like). In fact, due to the lack of a call guarantee, __del__
should almost never be used; use it with caution!__del__
always called upon completion of the interpreter.__init__
and __del__
in action: from os.path import join class FileObject: ''' , , .''' def __init__(self, filepath='~', filename='sample.txt'): # filename filepath self.file = open(join(filepath, filename), 'r+') def __del__(self): self.file.close() del self.file
if instance.equals(other_instance): # do something
__eq__
, in this case), and so accurately express what we had in mind : if instance == other_instance: #do something
__cmp__(self, other)
__cmp__
should return a negative number if self < other
, zero if self == other
, and a positive number in the case of self > other
. But, usually, it is better to define every comparison that you need than to define them all in __cmp__
. But __cmp__
can be a good way to avoid repetition and increase clarity when all necessary comparisons are operated with one criterion.__eq__(self, other)
==
.__ne__(self, other)
!=
.__lt__(self, other)
<
.__gt__(self, other)
>
.__le__(self, other)
<=
.__ge__(self, other)
>=
. class Word(str): ''' , .''' def __new__(cls, word): # __new__, str # ( ) if ' ' in word: print "Value contains spaces. Truncating to first space." word = word[:word.index(' ')] # Word return str.__new__(cls, word) def __gt__(self, other): return len(self) > len(other) def __lt__(self, other): return len(self) < len(other) def __ge__(self, other): return len(self) >= len(other) def __le__(self, other): return len(self) <= len(other)
Word
(using Word('foo')
and Word('bar')
) and compare them in length. Note that we did not define __eq__
and __ne__
, as this will lead to strange behavior (for example, Word('foo') == Word('bar')
will be regarded as true). This does not make sense when testing for equivalence based on length, so we leave the standard equivalence test from str
.functools
module, which will determine all comparing methods, all you need to do is define only __eq__
and one more ( __gt__
, __lt__
, etc.) This feature is available starting from version 2.7 of Python, but if You are satisfied, you will save a lot of time and effort. To enable it, place @total_ordering
above your class definition.__pos__(self)
+some_object
)__neg__(self)
-some_object
)__abs__(self)
abs()
.__invert__(self)
~
operator. For an explanation of what he does, see the Wikipedia article on binary operators .__round__(self, n)
round()
function. n
is the number of decimal places to be rounded.__floor__(self)
math.floor()
, that is, rounding to the nearest smaller integer.__ceil__(self)
math.ceil()
, that is, rounding to the nearest larger integer.__trunc__(self)
math.trunc()
, that is, circumcision to integer.__add__(self, other)
__sub__(self, other)
__mul__(self, other)
__floordiv__(self, other)
//
.__div__(self, other)
__truediv__(self, other)
from __future__ import division
.__mod__(self, other)
%
.__divmod__(self, other)
divmod()
function.__pow__
**
.__lshift__(self, other)
<<
.__rshift__(self, other)
>>
.__and__(self, other)
&
operator.__or__(self, other)
|
.__xor__(self, other)
^
. some_object + other
other + some_object
other
as the first operand and self
as the second. In most cases, the result of the reflected operation is the same as its usual equivalent, so when defining __radd__
you can limit yourself to calling __add__
and that's all. Note that the object to the left of the operator ( other
in the example) should not have the usual, unreflected version of this method. In our example, some_object.__radd__
will be called only if __add__
not defined in the other
.__radd__(self, other)
__rsub__(self, other)
__rmul__(self, other)
__rfloordiv__(self, other)
//
.__rdiv__(self, other)
/
.__rtruediv__(self, other)
from __future__ import division
.__rmod__(self, other)
%
.__rdivmod__(self, other)
divmod()
function when divmod(other, self)
called.__rpow__
**
.__rlshift__(self, other)
<<
.__rrshift__(self, other)
>>
.__rand__(self, other)
&
operator.__ror__(self, other)
|
.__rxor__(self, other)
^
. x = 5 x += 1 # x = x + 1
a += b
, __iadd__
should return a + b
, which will be assigned to a
). Here is a list:__iadd__(self, other)
__isub__(self, other)
__imul__(self, other)
__ifloordiv__(self, other)
//=
.__idiv__(self, other)
/=
.__itruediv__(self, other)
from __future__ import division
.__imod_(self, other)
%=
.__ipow__
**=
.__ilshift__(self, other)
<<=
.__irshift__(self, other)
>>=
.__iand__(self, other)
&=
.__ior__(self, other)
|=
.__ixor__(self, other)
^=
.float()
. Here they are:__int__(self)
__long__(self)
__float__(self)
__complex__(self)
__oct__(self)
__hex__(self)
__index__(self)
__index__
.__trunc__(self)
math.trunc(self)
. Must return its value truncated to an integer type (usually long).__coerce__(self, other)
__coerce__
should return None
if type conversion is not possible. If the transformation is possible, it must return a pair (a tuple of 2 elements) of self
and other
, converted to the same type.__str__(self)
str()
function called on an instance of your class.__repr__(self)
repr()
function repr()
on an instance of your class. The main difference from str()
in the target audience. repr()
more intended for machine-oriented output (moreover, it often has to be valid Python code), and str()
intended for people to read.__unicode__(self)
unicode()
function called on an instance of your class. unicode()
is similar to str()
, but returns a string in unicode. Be careful: if the client calls str()
on an instance of your class, and you have only defined __unicode__()
, then this will not work. Try to always define __str__()
for the case when someone does not have the luxury of Unicode.__format__(self, formatstr)
"Hello, {0:abc}!".format(a)
will call a.__format__("abc")
. This can be useful for defining your own numeric or string types, which you may want to provide some special formatting options.__hash__(self)
hash()
function that is called for an instance of your class. The method should return an integer value that will be used to quickly compare keys in dictionaries. Note that in this case, it is usually necessary to determine __eq__
too. Follow this rule: a == b
implies hash(a) == hash(b)
.__nonzero__(self)
bool()
function called on an instance of your class. Should return True or False, depending on when you consider the instance corresponding to True or False.__dir__(self)
dir()
function called on an instance of your class. This method should return a list of attributes to the user. Normally, the definition of __dir__
not required, but it can be vital for the interactive use of your class, if you redefine __getattr__
or __getattribute__
(which you will meet in the next section), or otherwise dynamically create attributes.__sizeof__(self)
sys.getsizeof()
function sys.getsizeof()
on an instance of your class. The method should return the size of your object in bytes. It is mainly useful for classes defined in extensions to C, but it is still useful to know about it.__getattr__(self, name)
AttributeError
when you need it. However, this method is called only when trying to access a non-existent attribute, so this is not a very good solution for encapsulation.__setattr__(self, name, value)
__getattr__
, __setattr__
encapsulation solution. This method allows you to define a behavior for assigning a value to an attribute, whether the attribute exists or not. That is, you can define any rules for any attribute value changes. However, you must be careful how to use __setattr__
, see the bad case example at the end of this list.__delattr__
__setattr__
, but to remove attributes, instead of setting values. Here, the same precautions are required as in __setattr__
to avoid infinite recursion (calling del self.name
in the definition of __delattr__
will cause infinite recursion).__getattribute__(self, name)
__getattribute__
looks to the place among its colleagues __setattr__
and __delattr__
, but I would not recommend you to use it. __getattribute__
can be used only with classes of a new type (in new versions of Python, all classes are of a new type, and in old versions you can get such a class inherited from object
). ( , __getattr__(self, name)
). , ( __getattribute__
, ). , , __getattr__
, __getattribute__
AttributeError
. ( , ), , , ( , ) . def __setattr__(self, name, value): self.name = value # , , , # __setattr__(). # , self.__setattr__('name', value). # , , def __setattr__(self, name, value): self.__dict__[name] = value # #
super
, since not all classes have an attribute __dict__
): class AccessCounter(object): ''', value . , value.''' def __init__(self, val): super(AccessCounter, self).__setattr__('counter', 0) super(AccessCounter, self).__setattr__('value', val) def __setattr__(self, name, value): if name == 'value': super(AccessCounter, self).__setattr__('counter', self.counter + 1) # . # , # AttributeError(name) super(AccessCounter, self).__setattr__(name, value) def __delattr__(self, name): if name == 'value': super(AccessCounter, self).__setattr__('counter', self.counter + 1) super(AccessCounter, self).__delattr__(name)]
__len__
and__getitem__
(more about them further). The protocol of the container to be changed requires the same as the container that is not to be changed, plus __setitem__
and __delitem__
. And finally, if you want your objects to be iterated through iteration, you must determine __iter__
which iterator returns. This iterator must comply with an iterator protocol that requires methods __iter__
(returns itself) and next
.__len__(self)
__getitem__(self, key)
self[key]
. . : TypeError
KeyError
.__setitem__(self, key, value)
self[nkey] = value
. . , KeyError
TypeError
.__delitem__(self, key)
del self[key]
). . , .__iter__(self)
iter()
for x in container:
. __iter__
, self
.__reversed__(self)
reversed()
. . , .__contains__(self, item)
__contains__
it is intended for checking the belonging of an element with in
and not in
. You ask, why is this not part of the sequence protocol? Because when __contains__
not defined, Python simply iterates over the entire sequence element by element and returns True
if it finds the right one.__missing__(self, key)
__missing__
used when inheriting from dict
. Defines the behavior for each case when trying to get an item by a nonexistent key (for example, if I have a dictionary d
and I write d["george"]
when "george"
it is not a key in the dictionary, it is called d.__missing__("george")
). class FunctionalList: '''- : head, tail, init, last, drop, take.''' def __init__(self, values=None): if values is None: self.values = [] else: self.values = values def __len__(self): return len(self.values) def __getitem__(self, key): # , list return self.values[key] def __setitem__(self, key, value): self.values[key] = value def __delitem__(self, key): del self.values[key] def __iter__(self): return iter(self.values) def __reversed__(self): return FunctionalList(reversed(self.values)) def append(self, value): self.values.append(value) def head(self): # return self.values[0] def tail(self): # return self.values[1:] def init(self): # return self.values[:-1] def last(self): # return self.values[-1] def drop(self, n): # n return self.values[n:] def take(self, n): # n return self.values[:n]
Counter
, OrderedDict
, NamedTuple
.isinstance()
issubclass()
, . Here they are:__instancecheck__(self, instance)
isinstance(instance, class)
, .__subclasscheck__(self, subclass)
issubclass(subclass, class)
).__call__(self, [args...])
x()
means the same as x.__call__()
. Notice __call__
accepts an arbitrary number of arguments; that is, you can define __call__
it just like any other function that takes as many arguments as you need.__call__
In particular, it can be useful in classes whose instances often change their state. Invoking an instance can be an intuitive and elegant way to change the state of an object. An example would be a class representing the position of an object on a plane: class Entity: ''', . "", .''' def __init__(self, size, x, y): self.x, self.y = x, y self.size = size def __call__(self, x, y): ''' .''' self.x, self.y = x, y # ...
with
. The concept of context managers was not new to Python (it was implemented earlier as part of a library), but in PEP 343 it reached the status of a language construct. You could already see expressions with with
: with open('foo.txt') as bar: # - bar
with
. The behavior of the context manager is determined by two magic methods:__enter__(self)
with
. Notice that the return __enter__
value is the value that is being worked on internally with
.__exit__(self, exception_type, exception_value, traceback)
exception_type
, exception_value
, traceback
None
. , ; , , __exit__
True
. , , .__enter__
and __exit__
can be useful for specific classes with well-described and common behavior for setting them up and clearing resources. You can use these methods to create common context managers for different objects. Here is an example: class Closer: ''' close with-.''' def __init__(self, obj): self.obj = obj def __enter__(self): return self.obj # with- def __exit__(self, exception_type, exception_val, trace): try: self.obj.close() except AttributeError: # close print 'Not closable.' return True #
Closer
with an FTP connection (a socket that has a close method): >>> from magicmethods import Closer >>> from ftplib import FTP >>> with Closer(FTP('ftp.somesite.com')) as conn: ... conn.dir() ... # output omitted for brevity >>> conn.dir() # long AttributeError message, can't use a connection that's closed >>> with Closer(int(5)) as i: ... i += 1 ... Not closable. >>> i 6
contextlib.closing()
a context manager, which does roughly the same thing (without any handling of the case where the object has no method close()
).__get__
, __set__
or __delete__
. Let's look at these magical methods:__get__(self, instance, instance_class)
instance
This is an object for whose attribute descriptor the method is being called. owner
This is the type (class) of the object.__set__(self, instance, value)
instance
This is an object for whose attribute descriptor the method is being called. value
this value is set to the descriptor.__delete__(self, instance)
instance
This is the object that owns the handle. class Meter(object): ''' .''' def __init__(self, value=0.0): self.value = float(value) def __get__(self, instance, owner): return self.value def __set__(self, instance, value): self.value = float(value) class Foot(object): ''' .''' def __get__(self, instance, owner): return instance.meter * 3.2808 def __set__(self, instance, value): instance.meter = float(value) / 3.2808 class Distance(object): ''', , .''' meter = Meter() foot = Foot()
copy
. Fortunately, modules in Python do not have a reason, so we can not worry that they suddenly begin to copy themselves uncontrollably and soon Linux robots will flood the entire planet, but we must tell Python how to copy correctly.__copy__(self)
copy.copy()
. copy.copy()
— , , . , .__deepcopy__(self, memodict={})
copy.deepcopy()
. copy.deepcopy()
— . memodict
, , . - , copy.deepcopy()
memodict
.pickle
) it has its own protocol and its own magic methods. But for a start on how to serialize pickle with already existing data types (quietly skip if you already know).exec()
or reading the file. But this is at best risky: if you store important data in the text, it can be damaged or changed in many ways, in order to crash your program or, in general, run some dangerous code on your computer. Better use pickle: import pickle data = {'foo': [1, 2, 3], 'bar': ('Hello', 'world!'), 'baz': True} jar = open('data.pkl', 'wb') pickle.dump(data, jar) # jar jar.close()
import pickle pkl_file = open('data.pkl', 'rb') # data = pickle.load(pkl_file) # print data pkl_file.close()
data
as if it had always been there.__getinitargs__(self)
__init__
, you can specify __getinitargs__
which argument sequence should return, which will be sent to __init__
. Note that this method only works with old-style classes.__getnewargs__(self)
__new__
during deserialization. This method should also return a tuple of arguments that will be sent to __new__
.__getstate__(self)
__dict__
, , . __setstate__
.__setstate__(self, state)
__setstate__
, , __dict__
. __getstate__
: , , .__reduce__(self)
__reduce__()
, . , , , . 2 5 : , , , , , __setstate__
(), () ().__reduce_ex__(self, protocol)
__reduce__
. And this can be achieved by implementing instead __reduce_ex__
. If __reduce_ex__
implemented, preference is given to it when it is called (you must still implement it __reduce__
for backward compatibility).Slate
), which remembers what and when it was written on it. However, specifically this board becomes clean every time it is serialized: the current value is not saved. import time class Slate: ''', . .''' def __init__(self, value): self.value = value self.last_change = time.asctime() self.history = {} def change(self, new_value): # . . self.history[self.last_change] = self.value self.value = new_value self.last_change = time.asctime() def print_changes(self): print 'Changelog for Slate object:' for k, v in self.history.items(): print '%s\t %s' % (k, v) def __getstate__(self): # self.value or self.last_change. # " " . return self.history def __setstate__(self, state): self.history = state self.value, self.last_change = None, None
Magic method | When it is called (example) | Explanation |
---|---|---|
__new__(cls [,...]) | instance = MyClass(arg1, arg2) | __new__ called when creating an instance |
__init__(self [,...]) | instance = MyClass(arg1, arg2) | __init__ called when creating an instance |
__cmp__(self, other) | self == other , self > other , Etc. | Called for any comparison. |
__pos__(self) | +self | Unary plus sign |
__neg__(self) | -self | Unary minus sign |
__invert__(self) | ~self | Bitwise inversion |
__index__(self) | x[self] | Transformation when an object is used as an index |
__nonzero__(self) | bool(self) , if self: | Object Boolean |
__getattr__(self, name) | self.name # name | Trying to get a non-existent attribute. |
__setattr__(self, name, val) | self.name = val | Assigning to any attribute |
__delattr__(self, name) | del self.name | Attribute removal |
__getattribute__(self, name) | self.name | Get any attribute |
__getitem__(self, key) | self[key] | Getting an item through the index |
__setitem__(self, key, val) | self[key] = val | Assignment to an element through an index |
__delitem__(self, key) | del self[key] | Deleting an item by index |
__iter__(self) | for x in self | Iteration |
__contains__(self, value) | value in self , value not in self | Affiliation checking with in |
__call__(self [,...]) | self(args) | "Call" instance |
__enter__(self) | with self as x: | with |
__exit__(self, exc, val, trace) | with self as x: | with |
__getstate__(self) | pickle.dump(pkl_file, self) | Serialization |
__setstate__(self) | data = pickle.load(pkl_file) | Serialization |
__unicode__
, __bytes__
( __str__
__unicode__
2.7) .__div__
.__coerce__
, - .__cmp__
, - .__nonzero__
__bool__
.next
__next__
.Source: https://habr.com/ru/post/186608/
All Articles