Pdf Classes

1 view
Skip to first unread message

Victoria Steigerwald

unread,
Aug 3, 2024, 4:22:03 PM8/3/24
to bromigcafe

Classes provide a means of bundling data and functionality together. Creatinga new class creates a new type of object, allowing new instances of thattype to be made. Each class instance can have attributes attached to it formaintaining its state. Class instances can also have methods (defined by itsclass) for modifying its state.

(Lacking universally accepted terminology to talk about classes, I will makeoccasional use of Smalltalk and C++ terms. I would use Modula-3 terms, sinceits object-oriented semantics are closer to those of Python than C++, but Iexpect that few readers have heard of it.)

Attributes may be read-only or writable. In the latter case, assignment toattributes is possible. Module attributes are writable: you can writemodname.the_answer = 42. Writable attributes may also be deleted with thedel statement. For example, del modname.the_answer will removethe attribute the_answer from the object named by modname.

Namespaces are created at different moments and have different lifetimes. Thenamespace containing the built-in names is created when the Python interpreterstarts up, and is never deleted. The global namespace for a module is createdwhen the module definition is read in; normally, module namespaces also lastuntil the interpreter quits. The statements executed by the top-levelinvocation of the interpreter, either read from a script file or interactively,are considered part of a module called __main__, so they have their ownglobal namespace. (The built-in names actually also live in a module; this iscalled builtins.)

The local namespace for a function is created when the function is called, anddeleted when the function returns or raises an exception that is not handledwithin the function. (Actually, forgetting would be a better way to describewhat actually happens.) Of course, recursive invocations each have their ownlocal namespace.

The global statement can be used to indicate that particularvariables live in the global scope and should be rebound there; thenonlocal statement indicates that particular variables live inan enclosing scope and should be rebound there.

Class definitions, like function definitions (def statements) must beexecuted before they have any effect. (You could conceivably place a classdefinition in a branch of an if statement, or inside a function.)

then MyClass.i and MyClass.f are valid attribute references, returningan integer and a function object, respectively. Class attributes can also beassigned to, so you can change the value of MyClass.i by assignment.__doc__ is also a valid attribute, returning the docstring belonging tothe class: "A simple example class".

In the MyClass example, this will return the string 'hello world'.However, it is not necessary to call a method right away: x.f is a methodobject, and can be stored away and called at a later time. For example:

As discussed in A Word About Names and Objects, shared data can have possibly surprisingeffects with involving mutable objects such as lists and dictionaries.For example, the tricks list in the following code should not be used as aclass variable because just a single list would be shared by all Doginstances:

There is no shorthand for referencing data attributes (or other methods!) fromwithin methods. I find that this actually increases the readability of methods:there is no chance of confusing local variables and instance variables whenglancing through a method.

Often, the first argument of a method is called self. This is nothing morethan a convention: the name self has absolutely no special meaning toPython. Note, however, that by not following the convention your code may beless readable to other Python programmers, and it is also conceivable that aclass browser program might be written that relies upon such a convention.

Any function object that is a class attribute defines a method for instances ofthat class. It is not necessary that the function definition is textuallyenclosed in the class definition: assigning a function object to a localvariable in the class is also ok. For example:

The name BaseClassName must be defined in anamespace accessible from the scope containing thederived class definition. In place of a base class name, other arbitraryexpressions are also allowed. This can be useful, for example, when the baseclass is defined in another module:

Execution of a derived class definition proceeds the same as for a base class.When the class object is constructed, the base class is remembered. This isused for resolving attribute references: if a requested attribute is not foundin the class, the search proceeds to look in the base class. This rule isapplied recursively if the base class itself is derived from some other class.

Derived classes may override methods of their base classes. Because methodshave no special privileges when calling other methods of the same object, amethod of a base class that calls another method defined in the same base classmay end up calling a method of a derived class that overrides it. (For C++programmers: all methods in Python are effectively virtual.)

An overriding method in a derived class may in fact want to extend rather thansimply replace the base class method of the same name. There is a simple way tocall the base class method directly: just call BaseClassName.methodname(self,arguments). This is occasionally useful to clients as well. (Note that thisonly works if the base class is accessible as BaseClassName in the globalscope.)

For most purposes, in the simplest cases, you can think of the search forattributes inherited from a parent class as depth-first, left-to-right, notsearching twice in the same class where there is an overlap in the hierarchy.Thus, if an attribute is not found in DerivedClassName, it is searchedfor in Base1, then (recursively) in the base classes of Base1,and if it was not found there, it was searched for in Base2, and so on.

In fact, it is slightly more complex than that; the method resolution orderchanges dynamically to support cooperative calls to super(). Thisapproach is known in some other multiple-inheritance languages ascall-next-method and is more powerful than the super call found insingle-inheritance languages.

Dynamic ordering is necessary because all cases of multiple inheritance exhibitone or more diamond relationships (where at least one of the parent classescan be accessed through multiple paths from the bottommost class). For example,all classes inherit from object, so any case of multiple inheritanceprovides more than one path to reach object. To keep the base classesfrom being accessed more than once, the dynamic algorithm linearizes the searchorder in a way that preserves the left-to-right ordering specified in eachclass, that calls each parent only once, and that is monotonic (meaning that aclass can be subclassed without affecting the precedence order of its parents).Taken together, these properties make it possible to design reliable andextensible classes with multiple inheritance. For more detail, seeThe Python 2.3 Method Resolution Order.

The above example would work even if MappingSubclass were to introduce a__update identifier since it is replaced with _Mapping__update in theMapping class and _MappingSubclass__update in the MappingSubclassclass respectively.

Note that the mangling rules are designed mostly to avoid accidents; it still ispossible to access or modify a variable that is considered private. This caneven be useful in special circumstances, such as in the debugger.

Notice that code passed to exec() or eval() does not consider theclassname of the invoking class to be the current class; this is similar to theeffect of the global statement, the effect of which is likewise restrictedto code that is byte-compiled together. The same restriction applies togetattr(), setattr() and delattr(), as well as when referencing__dict__ directly.

A piece of Python code that expects a particular abstract data type can often bepassed a class that emulates the methods of that data type instead. Forinstance, if you have a function that formats some data from a file object, youcan define a class with methods read() andreadline() that get thedata from a string buffer instead, and pass it as an argument.

This style of access is clear, concise, and convenient. The use of iteratorspervades and unifies Python. Behind the scenes, the for statementcalls iter() on the container object. The function returns an iteratorobject that defines the method __next__() which accesseselements in the container one at a time. When there are no more elements,__next__() raises a StopIteration exception which tells thefor loop to terminate. You can call the __next__() methodusing the next() built-in function; this example shows how it all works:

Generators are a simple and powerful tool for creating iterators. Theyare written like regular functions but use the yield statementwhenever they want to return data. Each time next() is called on it, thegenerator resumes where it left off (it remembers all the data values and whichstatement was last executed). An example shows that generators can be triviallyeasy to create:

Anything that can be done with generators can also be done with class-basediterators as described in the previous section. What makes generators socompact is that the __iter__() and __next__() methodsare created automatically.

Another key feature is that the local variables and execution state areautomatically saved between calls. This made the function easier to write andmuch more clear than an approach using instance variables like self.indexand self.data.

In addition to automatic method creation and saving program state, whengenerators terminate, they automatically raise StopIteration. Incombination, these features make it easy to create iterators with no more effortthan writing a regular function.

Some simple generators can be coded succinctly as expressions using a syntaxsimilar to list comprehensions but with parentheses instead of square brackets.These expressions are designed for situations where the generator is used rightaway by an enclosing function. Generator expressions are more compact but lessversatile than full generator definitions and tend to be more memory friendlythan equivalent list comprehensions.

c80f0f1006
Reply all
Reply to author
Forward
0 new messages