• No results found

1    Part 1 ­­ Beginning Python

1.8    Classes

Classes model the behavior of objects in the "real" world. Methods implement the  behaviors of these types of objects. Member variables hold (current) state. Classes enable us to implement new data types in Python.

The class: statement is used to define a class. The class: statement creates a class  object and binds it to a name.

1.8.1   A simple class

In [104]: class A:

   ...:     pass    ...:

In [105]: a = A()

To define a new style class (recommended), inherit from object or from another class  that does. Example:

In [21]: class A(object):

   ....:     pass    ....:

In [22]:

In [22]: a = A() In [23]: a

Out[23]: <__main__.A object at 0x82fbfcc>

1.8.2   Defining methods

A method is a function defined in class scope and with first parameter self:

In [106]: class B(object):

   ...:     def show(self):

   ...:         print 'hello from B'    ...:

In [107]: b = B() In [108]: b.show() hello from B

A method as we describe it here is more properly called an instance method, in order to  distinguish it from class methods and static methods.

1.8.3   The constructor

The constructor is a method named __init__.

Exercise: Define a class with a member variable name and a show method. Use print  to show the name. Solution:

In [109]: class A(object):

   ...:       def __init__(self, name):

   ...:       self.name = name    ...:       def show(self):

   ...:       print 'name: "%s"' % self.name    ...:

Notes:

The self variable is explicit. It references the current object, that is the object  whose method is currently executing.

The spelling ("self") is optional, but everyone spells it that way.

1.8.4   Member variables

Defining member variables ­­ Member variables are created with assignment. Example:

class A(object):

    def __init__(self, name):

        self.name = name

A small gotcha ­­ Do this:

In [28]: class A(object):

   ....:     def __init__(self, items=None):

   ....:         if items is None:

   ....:       self.items = []

   ....:         else:

   ....:       self.items = items

Do not do this:

In [29]: class B:

   ....:     def __init__(self, items=[]):   # wrong.  list ctor  evaluated only once.

   ....:         self.items = items

In the second example, the def statement and the list constructor are evaluated only  once. Therefore, the item member variable of all instances of class B, will share the same  value, which is most likely not what you want.

1.8.5   Calling methods

Use the instance and the dot operator.

Calling a method defined in the same class or a superclass:

From outside the class ­­ Use the instance:

some_object.some_method()

an_array_of_of_objects[1].another_method()

From within the same class ­­ Use self:

self.a_method()

From with a subclass when the method is in the superclass and there is a  method with the same name in the current class ­­ Use the class (name) or use  super:

SomeSuperClass.__init__(self, arg1, arg2)  super(CurrentClass,

self).__init__(arg1, arg2)

Calling a method defined in a specific superclass ­­ Use the class (name).

1.8.6   Adding inheritance

Referencing superclasses ­­ Use the built­in super or the explicit name of the  superclass. Use of super is preferred. For example:

In [39]: class B(A):

   ....:     def __init__(self, name, size):

   ....:         super(B, self).__init__(name)

   ....:         # A.__init__(self, name)    # an older alternative  form

   ....:         self.size = size

The use of super() may solve problems searching for the base class when using  multiple inheritance. A better solution is to not use multiple inheritance.

You can also use multiple inheritance. But, it can cause confusion. For example, in the  following, class C inherits from both A and B:

class C(A, B):

    ...

Python searches superclasses MRO (method resolution order). If only single inheritance  is involved, there is little confusion. If multiple inheritance is being used, the search order of super classes can get complex ­­ see here: 

http://www.python.org/download/releases/2.3/mro

For more information on inheritance, see the tutorial in the standard Python  documentation set: 9.5 Inheritance and 9.5.1 Multiple Inheritance.

Watch out for problems with inheriting from multiple classes that have a common base  class.

1.8.7   Class variables

Also called static data.

A class variable is shared by instances of the class.

Define at class level with assignment. Example:

class A(object):

    size = 5

    def get_size(self):

        return A.size

Reference with classname.variable.

Caution: self.variable = x creates a new member variable.

1.8.8   Class methods and static methods

Instance (plain) methods:

An instance method receives the instance as its first argument.

Class methods:

A class method receives the class as its first argument.

Define class methods with built­in function classmethod() or with decorator 

@classmethod.

See the description of classmethod() built­in function at "Built­in  Functions": http://docs.python.org/2/library/functions.html#classmethod Static methods:

A static method receives neither the instance nor the class as its first argument.

Define static methods with built­in function staticmethod() or with  decorator @staticmethod.

See the description of staticmethod() built­in function at "Built­in  Functions": http://docs.python.org/2/library/functions.html#staticmethod Notes on decorators:

A decorator of the form @afunc is the same as m = afunc(m). So, this:

@afunc

def m(self): pass

is the same as:

def m(self): pass m = afunc(m)

You can use decorators @classmethod and @staticmethod (instead of the  classmethod() and staticmethod() built­in functions) to declare class  methods and static methods.

Example:

class B(object):

    Count = 0

    def dup_string(x):

        s1 = '%s%s' % (x, x,)         return s1

    dup_string = staticmethod(dup_string)     @classmethod

    def show_count(cls, msg):

        print '%s  %d' % (msg, cls.Count, ) def test():

    print B.dup_string('abcd')

    B.show_count('here is the count: ')

An alternative way to implement "static methods" ­­ Use a "plain", module­level  function. For example:

In [1]: def inc_count():

   ...:         A.count += 1    ...:

In [2]:

In [2]: def dec_count():

   ...:         A.count ­= 1    ...:

In [3]:

In [3]: class A:

   ...:         count = 0

   ...:     def get_count(self):

   ...:       return A.count    ...:

In [4]:

In [4]: a = A()

In [5]: a.get_count() Out[5]: 0

In [6]:

In [6]:

In [6]: inc_count() In [7]: inc_count() In [8]: a.get_count() Out[8]: 2

In [9]:

In [9]: b = A()

In [10]: b.get_count() Out[10]: 2

1.8.9   Properties

The property built­in function enables us to write classes in a way that does not require a  user of the class to use getters and setters. Example:

class TestProperty(object):

    def __init__(self, description):

        self._description = description     def _set_description(self, description):

        print 'setting description'         self._description = description     def _get_description(self):

        print 'getting description'         return self._description

    description = property(_get_description, _set_description)

The property built­in function is also a decorator. So, the following is equivalent to  the above example:

class TestProperty(object):

    def __init__(self, description):

        self._description = description     @property

    def description(self):

        print 'getting description'         return self._description

    @description.setter

    def description(self, description):

        print 'setting description'         self._description = description

Notes:

We mark the instance variable as private by prefixing it with and underscore.

The name of the instance variable and the name of the property must be different. 

If they are not, we get recursion and an error.

For more information on properties, see Built­in Functions ­­ properties ­­ 

http://docs.python.org/2/library/functions.html#property

1.8.10   Interfaces

In Python, to implement an interface is to implement a method with a specific name and a specific arguments.

"Duck typing" ­­ If it walks like a duck and quacks like a duck ...

One way to define an "interface" is to define a class containing methods that have a  header and a doc string but no implementation.

Additional notes on interfaces:

Interfaces are not enforced.

A class does not have to implement all of an interface.

1.8.11   New­style classes

A new­style class is one that subclasses object or a class that subclasses object (that  is, another new­style class).

You can subclass Python's built­in data­types.

A simple example ­­ the following class extends the list data­type:

class C(list):

  def get_len(self):

    return len(self) c = C((11,22,33)) c.get_len()

c = C((11,22,33,44,55,66,77,88)) print c.get_len()

# Prints "8".

A slightly more complex example ­­ the following class extends the dictionary 

data­type:

class D(dict):

    def __init__(self, data=None, name='no_name'):

        if data is None:

      data = {}

        dict.__init__(self, data)         self.name = name

    def get_len(self):

        return len(self)     def get_keys(self):

        content = []

        for key in self:

      content.append(key)

        contentstr = ', '.join(content)         return contentstr

    def get_name(self):

        return self.name def test():

    d = D({'aa': 111, 'bb':222, 'cc':333})     # Prints "3"

    print d.get_len()

    # Prints "'aa, cc, bb'"

    print d.get_keys()     # Prints "no_name"

    print d.get_name()

Some things to remember about new­style classes:

In order to be new­style, a class must inherit (directly or indirectly) from 

object. Note that if you inherit from a built­in type, you get this automatically.

New­style classes unify types and classes.

You can subclass (built­in) types such as dict, str, list, file, etc.

The built­in types now provide factory functions: dict(), str(), int(),  file(), etc.

The built­in types are introspect­able ­­ Use x.__class__,  dir(x.__class__), isinstance(x, list), etc.

New­style classes give you properties and descriptors.

New­style classes enable you to define static methods. Actually, all classes enable you to do this.

A new­style class is a user­defined type. For an instance of a new­style class x,  type(x) is the same as x.__class__.

For more on new­style classes, see: http://www.python.org/doc/newstyle/

Exercises:

Write a class and a subclass of this class.

Give the superclass one member variable, a name, which can be entered when 

an instance is constructed.

Give the subclass one member variable, a description; the subclass constructor should allow entry of both name and description.

Put a show() method in the superclass and override the show() method in  the subclass.

Solution:

class A(object):

    def __init__(self, name):

        self.name = name     def show(self):

        print 'name: %s' % (self.name, ) class B(A):

    def __init__(self, name, desc):

        A.__init__(self, name)         self.desc = desc

    def show(self):

        A.show(self)

        print 'desc: %s' % (self.desc, )

1.8.12   Doc strings for classes

Add docstrings as a (triple­quoted) string beginning with the first line of a class. See  epydoc for a suggested format.

1.8.13   Private members

Add an leading underscore to a member name (method or data variable) to "suggest" that  the member is private.