Methods and Special Methods

We will begin by looking at a class that uses accessor methods to get and set the value of attributes, rather than using direct attribute access.

class Rectangle(object):

self.width = width self.height = height def getWidth(self): return self.width def setWidth(self, width): self.width = width def getHeight(self): return self.height def setHeight(self, height): self.height = height def area(self):

return self.getWidth() * self.getHeight()

We have chosen to use a Java-style naming convention for both getters and setters. Now we can write code like this:

rect = Rectangle(50, 10)

print rect.area() # Prints "500"

rect.setWidth(20)

We could just as easily have implemented the area() method like this:

def area(self):

return self.width * self.height

Writing trivial accessor methods as we have done here is the right approach for languages like C++ because it provides maximum flexibility, and no overhead in the compiled code. And if at a later stage we needed to perform some computation in an accessor, we can simply add in the functionality without requir ing users of our class to change their code. But in Python, it is not necessary to write such accessors. Instead, we can directly read and write attributes, and if at a later stage we need to perform some computation, we can use Python's property() function. This function allows us to create named properties that can replace attributes. Properties are accessed just like attributes, but behind the scenes they call the methods that we specify to get and set the value.

Here is a second version of the Rectangle class, this time using direct attribute access for the width and height, and a property for the area:

class Rectangle(object):

self.width = width self.height = height def _area(self):

return self.width * self.height area = property(fget=_area)

This allows us to write code like this: rect = Rectangle(5, 4)

print rect.width, rect.height, rect.area # Prints (5, 4, 20) rect.width = 6

Python's property() function can be used to specify a getter, a setter, a deletion method, and a docstring. Since we specified only a getter, the area property is read-only. If later on we needed to perform some computation when the width was accessed, we could simply turn it into a property, like this:

def _width(self):

return self._width def _setWidth(self, width): # Perform some computation self._width = width width = property(fget=_width, fset=_setWidth)

Discussion of private names

Notice that we have changed the name of the instance variable from width to

_width to avoid a name collision with the width property. In general, properties whose values are held in instance variables use private names (names with two leading underscores) for the instance variables, to avoid name collisions with the property name that the class's user uses. For example, users of the Rectangle class with the width property, can get and set the width attribute exactly the same as before, only now, the _width() and _setWidth() methods are used behind the scenes to perform these operations, and the attribute's data is held in the __width instance variable.

Table 3.1 Basic Special Methods

Method Syntax

_nonzero_(self)

_unicode_(self)

pass y = eval(NxN

print x print x

Description

Initializes a newly created instance

Makes instances callable, that is, turns them into functors. The args are optional. (Advanced)

Returns-1 if self < other, 0 if they are equal, and 1 otherwise. If_cmp_()

is implemented, it will be used for any comparison operators that are not explicitly implemented.

Returns True if x is equal to y

Returns True if x is not equal to y

Returns True if x is less than or equal to y

Returns True if x is less than y

Returns True if x is greater than or equal to y

Returns True if x is greater than y Returns True if x is nonzero

Returns an eval() -able representation of x. Using backticks is the same as calling repr().*

Returns a human-readable representation of x

Returns a human-readable Unicode representation of x

Python offers even more control over attribute access than we have shown here, but since this is not necessary to our goal of GUI programming, we will leave this as a topic to look up if it ever becomes of interest. The starting point is the documentation for the_getattr_(),_getattribute_(), and_setat-

tr_() special methods.

The mechanics of Python methods, including special methods, are exactly the same as for functions, but with the addition of the self first argument, and the ability to access self's attributes and call self's methods. We just have to remember that when we call methods or access instance variables, we must specify the instance using self. For example, in the Rectangle class's setHeight()

★It is best to use repr() rather than backticks since backticks will be dropped in Python 3.0.

method, we used self.height to refer to the instance variable and plain height to refer to the parameter, that is, to a local variable. Similarly, in the area() method, we call two Rectangle methods, again using self. This is quite different from C++ and Java, where the instance is assumed.

In C++ it is possible to implement operators, that is, to provide our own implementations of operators for our data types. The C++ syntax uses the keyword operator followed by the operator itself—for example, operator+()—but in Python every operator has a name, so to implement a class's + operator in

Python we would implement an_add_() method. All the Python methods for implementing operators are special methods, and this is signified by them having names that begin and end with two underscores.

To better integrate our custom classes into Python, there are some additional general special methods which may be worth implementing. For example, we might want to provide support for the comparison operators, and a Boolean value for instances of our class. We will add a few more methods to the Rectangle class to show the possibilities in action, but for brevity we won't repeat the class statement and the methods we have already implemented. We will start with comparisons:

return cmp(self.area(), other.area())

If we want we can implement a special method for every one of the comparison operators. For example, if we implement_lt_() "less than", we will be able to compare instances of our class with the < operator. However, if we don't want to implement the comparison operators individually, we can simply implement

_cmp_() as we have done here. Python will use the specific special method for comparisons if it has been implemented, but will fall back on_cmp_() otherwise. So just by implementing this one special method, all the comparison operators (<, <=, ==, !=, >=, >) will work with Rectangle objects:

rectA == rectB # True because both have the same area rectA < rectB # False

We used the built-in cmp() function to implement_cmp_(). The cmp() function takes two objects and returns -1 if the first is less than the second, 0 if they are equal, and 1 otherwise. We used the rectangles' areas as the basis for comparison, which is why we got the rather surprising True result in our example. A stricter, and perhaps better implementation might be:

return cmp(self.width, other.width) return cmp(self.height, other.height)

Here we return the result of comparing the heights if the widths are the same; otherwise, we return the result of comparing the widths.

If we do not reimplement any comparison special methods, in most cases Python will happily perform comparisons for us, although not necessarily in the way we would want. If we are creating a class where comparisons make sense, we ought to implement __cmp__() . For other classes, the safest thing to do is to implement_cmp_() withabodyof return NotlmplementedError.

def _nonzero_(self):

return self.width or self.height

This special method is used when the object is in a Boolean context; for example, bool(rectA), or if rectB: and returns True if the object is "nonzero".

return "Rectangle(%d, %d)" % (self.width, self.height)

The "representation" special method must return a string which, if evaluated (e.g., using eval()), will result in the construction of an object with the same properties as the object it is called on. Some objects are too complex to support this, and for some objects, such as a window or a button in a GUI, it doesn't make sense; so such classes don't provide a_repr_() implementation. In a string % operator's format string we use %r to get the result of this special method; we can also use the repr() function or the backticksNN operator. Back-ticks are just a syntactic alternative to using repr(). For example, repr(x) and NxN both return identical results: the representation of object x as returned by x's_repr_() method.

There is also a_str_() special method that must return a string representation of the object it is called on (like Java's toString() method), but unlike

_repr_(), the representation is meant to be human-readable and does not have to be eval() -able. If, as in this case, the_str_() method is not implemented, Python will use the_repr_() method instead. For example:

print rect # Prints "Rectangle(8, 9)" using _repr_()

If we want a human-readable Unicode string representation of our class, we can implement __unicode__() .

There are a few more general special methods that we could implement, but which are not appropriate for the Rectangle class. All the commonly implemented general special methods are listed in Table 3.1.

At this point, C++ programmers might be wondering where the copy constructor and assignment operators are, and Java programmers might be wondering about the clone() method. Python does not use a copy constructor and reimplementing the assignment operator is not necessary. If we want to do an assignment we just use = and Python will bind a new name to our existing

Table 3.2 Selected Numeric Special Methods

Method

Syntax

Method

Syntax

_float_(self)

float(x)

_int_(self)

int(x)

_abs_(self)

abs(x)

_neg_(self)

-x

_add_(self, other)

x + y

_sub_(self, other)

x - y

_iadd_(self, other)

x += y

_isub_(self, other)

x -= y

_radd_(self, other)

y + x

_rsub_(self, other)

y - x

mul (self, other)

x * y

_mod_(self, other)

x % y

imul (self, other)

x *= y

imod (self, other)

x %= y

rmul (self, other)

y * x

rmod (self, other)

y % x

floordiv (self, other)

x // y

truediv (self, other)

x / y

ifloordiv (self, other)

x //= y

itruediv (self, other)

x /= y

rfloordiv (self, other)

y // x

rtruediv (self, other)

y / x

object. If we really do need a copy of our object, we can use the copy() or deep-copy() function from the copy module, the first for objects that don't have nested attributes or when a shallow copy suffices, and the second for objects that must be copied in full. Alternatively, we can provide our own copy method, which we usually call copy() since this is conventional Python practice.

For numerical classes, it is often convenient to provide functionality to support the standard numeric operators, such as + and +=. This is achieved in Python's usual way, by implementing various special methods. If we implement only +, Python will use it to provide +=, but it is often best to implement both since that gives us finer control and makes it easier to optimize the operations.

The most commonly implemented numeric special methods are listed in Table 3.2. Those not listed include bit-shifting operators and hexadecimal and octal conversion operators.

The reason for two different division operators is that Python can perform either integer or floating-point division, as explained on page 17.

Some special methods have two or three versions; for example,_add_(),

_radd_(), and_iadd_(). The "r" versions (e.g.,_radd_()), are for situations where the left-hand operand does not have a suitable method, but the right-hand operand does. For example, if we have the expression x + y, with x and y of types X and Y, Python will first try to evaluate the expression by calling

X._add_(x, y). But if type X does not have this method, Python will then try

Y._radd_(x, y).If Y has no such method, an exception will be raised.

In the "i" versions, the "i" stands for "in-place". They are used for augmented assignment operators such as +=. We will shortly see an example that shows many of these methods in practice, but first we must learn how to create static data and static methods.

Was this article helpful?

0 0
Tuberminator

Tuberminator

The main focus of this report is to show how to get involved in video marketing on the run, how to rank quickly on YouTube and Google using FREE semi-automatic tools and services. QUICKLY AND FREE. I will show methods and techniques I use to rank my videos, as well as free resources and tools to make video clips, to get backlinks and free traffic.

Get My Free Ebook


Post a comment