Table of Contents
Definition
class A:
def self_foo(self):
pass
@staticmethod
def static_new():
pass
@classmethod
def classmethod_new(cls):
pass
When a class definition is executed, the following steps occur:
- MRO entries are resolved;
- the appropriate metaclass is determined;
- the class namespace is prepared;
- the class body is executed;
- the class object is created.
1.MRO
Method Resolution Order is the order in which base classes are searched for a member during lookup. See Python 2.3 Method Resolution Order for details.
A Demo
from socketserver import ThreadingMixIn
from wsgiref.simple_server import WSGIServer
class ThreadingServer(ThreadingMixIn, WSGIServer):
pass
class ThreadingMixIn:
"""Mix-in class to handle each request in a new thread."""
# Decides how threads will act upon termination of the
# main process
daemon_threads = False
def process_request_thread(self, request, client_address):
"""Same as in BaseServer but as a thread.
In addition, exception handling is done here.
"""
try:
self.finish_request(request, client_address)
self.shutdown_request(request)
except:
self.handle_error(request, client_address)
self.shutdown_request(request)
def process_request(self, request, client_address):
"""Start a new thread to process the request."""
t = threading.Thread(target = self.process_request_thread,
args = (request, client_address))
t.daemon = self.daemon_threads
t.start()
ThreadingMinIn only have 2 methods, but calls 4 methods.
list(filter(lambda attr: not attr.startswith('_'), dir(WSGIServer)))
ThreadingServer.__mro__
(__main__.ThreadingServer,
socketserver.ThreadingMixIn,
wsgiref.simple_server.WSGIServer,
http.server.HTTPServer,
socketserver.TCPServer,
socketserver.BaseServer,
object)
2.Determining the appropriate metaclass
3.Preparing the class namespace
- namespace = metaclass.__prepare__(name, bases, **kwds), should be a dict
- The namespace returned by __prepare__ is passed in to __new__,
- but when the final class object is created the namespace is copied into a new dict.
4.Executing the class body
-
The class body is executed (approximately) as exec(body, globals(), namespace).
-
A
exec
demo
tmp_namespace = {}
def say_hi():
return 'hi'
tmp_global = {'say_hi': say_hi}
exec('a=1; b=2; hi=say_hi();print(4+5*2)', tmp_global, tmp_namespace)
print('---tmp_namespace', tmp_namespace)
# print('---tmp_global:', tmp_global)
14
---tmp_namespace {'a': 1, 'b': 2, 'hi': 'hi'}
exec(object[, globals[, locals]])
- If globals and locals are given, they are used for the global and local variables, respectively.
- Remember that at module level, globals and locals are the same dictionary.
- If exec gets two separate objects as globals and locals, the code will be executed as if it were embedded in a class definition
5.Creating the class object
-
The class object is created by calling metaclass(name, bases, namespace, **kwds)
-
After the class object is created, it is passed to the class decorators included in the class definition (if any) and the resulting object is bound in the local namespace as the defined class.
-
A demo: create class object manually
def foo1(self):
print("foo1")
pass
tmp_namespace = {'foo1':foo1, 'name': 'hi'}
FooClass=type('FooClass', (object,), tmp_namespace)
f = FooClass()
f.foo1()
print(f.name)
foo1
hi
Special methods
Basic customization
- object.__new__(cls[, …])
- If __new__() is invoked during object construction and it returns an instance or subclass of cls, then the new instance’s __init__() method will be invoked like __init__(self[, …]), where self is the new instance and the remaining arguments are the same as were passed to the object constructor.
- If __new__() does not return an instance of cls, then the new instance’s __init__() method will not be invoked.
- __new__() is intended mainly to allow subclasses of immutable types (like int, str, or tuple) to customize instance creation. It is also commonly overridden in custom metaclasses in order to customize class creation.
- object.__del__(self)
- Called when the instance is about to be destroyed.
- If a base class has a del() method, the derived class’s del() method, if any, must explicitly call it to ensure proper deletion of the base class part of the instance.
- object.__repr__(self)
- Called by the repr() built-in function to compute the “official” string representation of an object
- If a class defines repr() but not str(), then repr() is also used when an “informal” string representation of instances of that class is required.
- object.__str__(self)
- Called by str(object) and the built-in functions format() and print() to compute the “informal” or nicely printable string representation of an object
- The default implementation defined by the built-in type object calls object.__repr__().
- “rich comparison” methods
- object.__lt__(self, other)
- object.__le__(self, other)
- object.__eq__(self, other)
- object.__ne__(self, other)
- object.__gt__(self, other)
- object.__ge__(self, other)
- object.__hash__(self)
- Called by built-in function hash()
- it is advised to mix together the hash values of the components of the object
- Changing hash values affects the iteration order of sets
- …
- object.__bool__(self)
- Called to implement truth value testing and the built-in operation bool();
- should return False or True. When this method is not defined, len() is called, if it is defined, and the object is considered true if its result is nonzero.
- If a class defines neither len() nor bool(), all its instances are considered true.
Customizing attribute access
1. object.__getattr__(self, name)
- Called when the default attribute access fails
- __getattribute__() raises an AttributeError because name is:
- not an instance attribute
- or not an attribute in the class tree for self
- __get__() of a name property raises AttributeError
- __getattribute__() raises an AttributeError because name is:
- a way to access other attributes of the instance
Demo
class SqliteDB:
pass
class MySqlDB:
def select(self, sql):
print('select')
def update(self, sql):
print('update')
def delete(self, sql):
print('delete')
class DBProxy:
def __init__(self, real_db):
self._real_db = real_db
print("===", self._real_db.__dict__)
def __getattr__(self, name):
return getattr(self._real_db, name)
db = DBProxy(MySqlDB())
db.select('test')
db.update('test')
db.delete('test')
=== {}
select
update
delete
2.object.__getattribute__(self, name)
- Called unconditionally to implement attribute accesses for instances of the class.
- If the class also defines __getattr__(), the latter will not be called unless __getattribute__() either calls it explicitly or raises an AttributeError.
- In order to avoid infinite recursion in this method, its implementation should always call the base class method with the same name to access any attributes it needs, for example, object.__getattribute__(self, name).
3.object.__setattr__(self, name, value)
- Called when an attribute assignment is attempted.
- If __setattr__() wants to assign to an instance attribute, it should call the base class method with the same name, for example, object.__setattr__(self, name, value).
4.object.__delattr__(self, name)
- Like setattr() but for attribute deletion instead of assignment. This should only be implemented if del obj.name is meaningful for the object.
5.object.__dir__(self)
- Called when dir() is called on the object. A sequence must be returned. dir() converts the returned sequence to a list and sorts it.
Emulating callable objects
object.__call__(self[, args…])
- Called when the instance is “called” as a function;
- if this method is defined, x(arg1, arg2, …) is a shorthand for x.call(arg1, arg2, …).
Emulating container types
-
object.__len__(self)
-
object.__length_hint__(self)
-
object.__getitem__(self, key)
- Called to implement evaluation of self[key]
-
object.__setitem__(self, key, value)
- Called to implement assignment to self[key]
-
object.__delitem__(self, key)
- Called to implement deletion of self[key].
-
object.__missing__(self, key)
- Called by dict.__getitem__() to implement self[key] for dict subclasses when key is not in the dictionary.
-
object.__iter__(self)
- This method is called when an iterator is required for a container.
- This method should return a new iterator object that can iterate over all the objects in the container.
- For mappings, it should iterate over the keys of the container.
- Iterator objects also need to implement this method; they are required to return themselves. For more information on iterator objects, see Iterator Types.
-
object.__reversed__(self)
- Called (if present) by the reversed() built-in to implement reverse iteration.
- It should return a new iterator object that iterates over all the objects in the container in reverse order.
-
object.__contains__(self, item)
- Called to implement membership test operators. Should return true if item is in self, false otherwise.
Emulating numeric types
- object.__add__(self, other)
- object.__sub__(self, other)
- object.__mul__(self, other)
- …
(+, -, *, @, /, //, %, divmod(), pow(), **, <<, >>, &, ^, |).
(+=, -=, *=, @=, /=, //=, %=, **=, <<=, >>=, &=, ^=, |=).
…
With Statement Context Managers
A context manager is an object that defines the runtime context to be established when executing a with statement
- object.__enter__(self)
- Enter the runtime context related to this object.
- The with statement will bind this method’s return value to the target(s) specified in the as clause of the statement, if any.
- object.__exit__(self, exc_type, exc_value, traceback)
- Exit the runtime context related to this object.
- The parameters describe the exception that caused the context to be exited.
- If the context was exited without an exception, all three arguments will be None.
- If an exception is supplied, and the method wishes to suppress the exception (i.e., prevent it from being propagated), it should return a true value. Otherwise, the exception will be processed normally upon exit from this method.
- Should not reraise the passed-in exception; this is the caller’s responsibility.
Demo: python RLock implement code fragment
class RLock:
def __init__(self):
pass
def acquire(self, blocking=True, timeout=-1):
print("---fake acquire")
pass
__enter__ = acquire
def release(self):
print("---fake release")
pass
def __exit__(self, t, v, tb):
self.release()
lock = RLock()
lock.acquire()
print('Do someting')
lock.release()
---fake acquire
Do someting
---fake release
Simplifier and safer by:
with RLock():
print('Do someting')
---fake acquire
Do someting
---fake release
Another method
from contextlib import contextmanager
class RLock_A:
def __init__(self):
pass
def acquire(self, blocking=True, timeout=-1):
print("---fake acquire")
pass
def release(self):
print("---fake release")
pass
@contextmanager
def lock(self):
try:
self.acquire()
yield
finally:
self.release()
A = RLock_A()
with A.lock():
print('Do someting')
---fake acquire
Do someting
---fake release
Magic functions
__slots__
- Decrease memory usage
- Cannot add attributes dynamically
Demo
class Student(object):
__slots__ = ('name', 'age')
s = Student()
s.name = 'Who'
s.age = 23
s.score=90
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-3-103cb7d42de3> in <module>
----> 1 s.score=90
AttributeError: 'Student' object has no attribute 'score'
Multiple consturctor
class Date:
def __init__(self, y, m, d):
self.y = y
self.m = m
self.d = d
def __repr__(self):
return '{}-{}-{}'.format(self.y, self.m, self.d)
@classmethod
def now(cls):
from datetime import datetime
now = datetime.now()
return cls(now.year, now.month, now.day)
print(Date(2008, 7, 1))
print(Date.now())
2008-7-1
2020-3-23