Book Image

Python Unlocked

By : Arun Tigeraniya
Book Image

Python Unlocked

By: Arun Tigeraniya

Overview of this book

Python is a versatile programming language that can be used for a wide range of technical tasks—computation, statistics, data analysis, game development, and more. Though Python is easy to learn, it’s range of features means there are many aspects of it that even experienced Python developers don’t know about. Even if you’re confident with the basics, its logic and syntax, by digging deeper you can work much more effectively with Python – and get more from the language. Python Unlocked walks you through the most effective techniques and best practices for high performance Python programming - showing you how to make the most of the Python language. You’ll get to know objects and functions inside and out, and will learn how to use them to your advantage in your programming projects. You will also find out how to work with a range of design patterns including abstract factory, singleton, strategy pattern, all of which will help make programming with Python much more efficient. Finally, as the process of writing a program is never complete without testing it, you will learn to test threaded applications and run parallel tests. If you want the edge when it comes to Python, use this book to unlock the secrets of smarter Python programming.
Table of Contents (15 chapters)
Python Unlocked
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface
Index

Playing with attributes


Key 8: Which attribute will be used.

Attributes are values that are associated with an object that can be referenced by name using dotted expressions. It is important to understand how attributes of an object are found. The following is the sequence that is used to search an attribute:

  1. If an attribute is a special method, and it exists in the object's type (or bases), return it, for example: __call__, __str__, and __init__. When these methods are searched, their behavior is only in the instance's type:

    >>> class C:
    ...     def __str__(self,):
    ...             return 'Class String'
    ...     def do(self):
    ...             return 'Class method'
    ... 
    >>> c = C()
    >>> print(c)
    Class String
    >>> print(c.do())
    Class method
    >>> def strf(*args):
    ...     return 'Instance String',args
    ... 
    >>> def doo(*args):
    ...     return 'Instance Method'
    ... 
    >>> c.do = doo
    >>> c.__str__ = strf
    >>> print(c)
    Class String
    >>> print(c.do())
    Instance Method
  2. If an object's type has a __getattribute__ attribute, then this method is invoked to get the attribute whether this attribute is present or not. It is the total responsibility of __getattribute__ to get the attribute. As shown in the following code snippet, even if the do method is present, it is not found as getattribute didn't return any attribute:

    >>> class C:
    ...     def do(self):
    ...             print("asdf")
    ...     def __getattribute__(self,attr):
    ...             raise AttributeError('object has no attribute "%s"'%attr)
    ... 
    >>> c = C()
    >>> c.do()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 5, in __getattribute__
    AttributeError: object has no attribute "do"
    >>> 
  3. Search in object's type __dict__ to find the attribute. If it is present, and it is data descriptor, return it:

    >>> class Desc:
    ...     def __init__(self, i):
    ...             self.i = i
    ...     def __get__(self, obj, objtype):
    ...             return self.i
    ...     def __set__(self,obj, value):
    ...             self.i = value
    ...
    >>> class C:
    ...     attx = Desc(23)
    ...
    >>> c = C()
    >>> c.attx
    23
    >>> c.__dict__['attx'] = 1234
    >>> c.attx
    23
    >>> C.attx = 12
    >>> c.attx
    1234
  4. Search in object's __dict__ type (and if this object is class, search bases __dict__ as well) to find the attribute. If the attribute is descriptor, return the result.

  5. Search in object's type__dict__ to find the attribute. If the attribute is found, return it. If it is non-data descriptor, return its result, and check in other bases using the same logic:

    >>> class Desc:
    ...     def __init__(self, i):
    ...             self.i = i
    ...     def __get__(self, obj, objtype):
    ...             return self.i
    ...
    >>> class C:
    ...     attx = Desc(23)
    ...
    >>> c = C()
    >>> c.attx
    23
    >>> c.__dict__['attx'] = 34
    >>> c.attx
    34
  6. If object type's __getattr__ is defined, check whether it can give us the attribute:

    >>> class C:
    ...     def __getattr__(self, key):
    ...             return key+'_#'
    ...
    >>> c = C()
    >>> c.asdf
    'asdf_#'
  7. Raise AttributeError.

Descriptors

Key 9: Making custom behavior attributes.

Any attribute of a class, which is an object defining any of these methods, acts as a descriptor:

  • __get__(self, obj, type=None) --> value

  • __set__(self, obj, value) --> None

  • __delete__(self, obj) --> None

When an attribute is searched in an object first, it is searched in its dictionary then its type's (base class's) dictionary. If found, object has one of these methods defined and that method is invoked instead. Let's assume that b is an instance of the B class, then the following will happen:

  • Invocation through class is type.__getattribute__() transforming to B.__dict__['x'].__get__(None, B)

  • Invocation through instance is object .__getattribute__() --> type(b).__dict__['x'].__get__(b, type(b))

Objects with only __get__ are non-data descriptors, and objects that include __set__ / __del__ are data descriptors. Data descriptors take precedence over instance attributes, whereas non-data descriptors do not.

Class, static, and instance methods

Key 10: Implementing class method and static method.

Class, static, and instance methods are all implementable using descriptors. We can understand descriptors and these methods in one go:

  • Class methods are methods that always get class as their first argument and they can be executed without any instance of class.

  • Static methods are methods that do not get any implicit objects as first argument when executed via class or instance.

  • Instance methods get instances when called via instance but no implicit argument when called via class.

A sample code usage of these methods is as follows:

>>> class C:
...     @staticmethod
...     def sdo(*args):
...             print(args)
...     @classmethod
...     def cdo(*args):
...             print(args)
...     def do(*args):
...             print(args)
...
>>> ic = C()
# staticmethod called through class: no implicit argument is passed
>>> C.sdo(1,2)
(1, 2)
# staticmethod called through instance:no implicit argument is passed
>>> ic.sdo(1,2)(1, 2)
# classmethod called through instance: first argument implicitly class
>>> ic.cdo(1,2)
(<class '__main__.C'>, 1, 2)
# classmethod called through class: first argument implicitly class
>>> C.cdo(1,2)
(<class '__main__.C'>, 1, 2)
# instancemethod called through instance: first argument implicitly instance
>>> ic.do(1,2)
(<__main__.C object at 0x00DC9E30>, 1, 2)
#instancemethod called through class: no implicit argument, acts like static method.
>>> C.do(1,2)
(1, 2)

They can be understood and implemented using descriptors easily as follows:

from functools import partial
>>> class my_instancemethod:
...     def __init__(self, f):
...         # we store reference to function in instance
...         # for future reference
...         self.f = f
...     def __get__(self, obj, objtype):
...         # obj is None when called from class
...         # objtype is always present
...         if obj is not None:
...             return partial(self.f,obj)
...         else: # called from class
...             return self.f
...
>>> class my_classmethod:
...     def __init__(self, f):
...         self.f = f
...     def __get__(self, obj, objtype):
...         # we pass objtype i.e class object
...         # when called from instance or class
...         return partial(self.f,objtype)
...
>>> class my_staticmethod:
...     def __init__(self, f):
...         self.f = f
...     def __get__(self, obj, objtype):
...         # we do not pass anything
...         # for both conditions
...         return self.f
...
>>> class C:
...     @my_instancemethod
...     def ido(*args):
...         print("imethod",args)
...     @my_classmethod
...     def cdo(*args):
...         print("cmethod",args)
...     @my_staticmethod
...     def sdo(*args):
...         print("smethod",args)
...
>>> c = C()
>>> c.ido(1,2)
imethod (<__main__.C object at 0x00D7CBD0>, 1, 2)
>>> C.ido(1,2)
imethod (1, 2)
>>> c.cdo(1,2)
cmethod (<class '__main__.C'>, 1, 2)
>>> C.cdo(1,2)
cmethod (<class '__main__.C'>, 1, 2)
>>> c.sdo(1,2)
smethod (1, 2)
>>> C.sdo(1,2)
smethod (1, 2)

Tip

Downloading the example code

You can download the example code files from your account at http://www.packtpub.com for all the Packt Publishing books you have purchased. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.