brief introduction
Class is a very important concept of object-oriented programming. python also has class and supports all standard features of object-oriented programming: inheritance, polymorphism, etc.
This article will explain the class information in Python in detail.
Scope and namespace
Before going into class in detail, let's look at the concepts of scope and namespace.
Namespaces are mappings from names to objects. Most namespaces are implemented through Python dictionaries.
Namespace is mainly used to avoid name conflicts in programs. As long as the name remains unique in the same namespace, the names in different command spaces do not affect each other.
There are three namespaces in Python:
- Built in names, built-in names of Python language, such as function name abs, char, Exception name BaseException, Exception, etc.
- global names, the name defined in the module, records the variables of the module, including functions, classes, other imported modules, module level variables and constants.
- local names, the name defined in the function, records the variables of the function, including the parameters of the function and locally defined variables. (also defined in the class)
The search order of namespaces is local name global name built-in name.
Namespaces created at different times have different lifetimes. Namespaces containing built-in names are created when the Python interpreter starts and will never be deleted. The global namespace of a module is created when the module definition is read in
Typically, the module namespace also continues until the interpreter exits.
Statements executed by the top-level call of the interpreter, such as programs read from a script file or programs read interactively, are considered to be__ main__ Modules are part of the call, so they also have their own global namespace. (the built-in name actually exists in a module; this module is called builtins.)
A scope is a text area of a Python program whose namespace is directly accessible.
There are four scopes in Python:
- Local: the innermost layer, which contains local variables, such as the interior of a function / method.
- Enclosing: contains non local and non global variables. For example, if two nested functions, A function (or class) A contains A function B, the scope in A is nonlocal for the name in B.
- Global: the outermost layer of the current script, such as the global variable of the current module.
- Built in: contains Built-in variables / keywords, etc., Last searched
The search order of the scope is local - > enclosing - > Global - > build in
In Python, the nonlocal keyword is used to declare the Enclosing scope, and the global keyword is used to declare the global scope.
Let's take an example of how global and nonlocal affect variable binding:
def scope_test(): def do_local(): spam = "local spam" def do_nonlocal(): nonlocal spam spam = "nonlocal spam" def do_global(): global spam spam = "global spam" spam = "test spam" do_local() print("After local assignment:", spam) do_nonlocal() print("After nonlocal assignment:", spam) do_global() print("After global assignment:", spam) scope_test() print("In global scope:", spam) Copy code
Above program output:
After local assignment: test spam After nonlocal assignment: nonlocal spam After global assignment: nonlocal spam In global scope: global spam Copy code
The variables in the function are local scope by default. If you want to modify the variables of the external function in the function, you need to declare this variable as nonlocal. Finally, the variables at the top level of the module or program file are global scope. If you need to modify by reference, you need to declare it as global scope.
class
Classes in Python are defined by class. Let's look at the simplest class definition:
class ClassName: <statement-1> . . . <statement-N> Copy code
The code in the class definition will create a new namespace, and the variables in it will be regarded as local scopes. All assignments to local variables are within this new namespace.
Class object
After class is defined, a class object is generated. We can access the properties and methods defined in the class through this class object.
For example, we define the following classes:
class MyClass: """A simple example class""" i = 12345 def f(self): return 'hello world' Copy code
Class defines an attribute i and a method f. Then we can use MyClass i and MyClass f to visit them.
Note that there is no variable access range control in Python, such as private and public in java. You can regard variables and methods in Python class as public.
We can go directly to MyClass i assignment to change the value of the i variable.
In [2]: MyClass.__doc__ Out[2]: 'A simple example class' In [3]: MyClass.i=100 In [4]: MyClass Out[4]: __main__.MyClass In [5]: MyClass.i Out[5]: 100 Copy code
In class, we also define the document of class, which can be used directly through__ doc__ To access.
Class
Instantiate a class object and treat the class as a parameterless function.
In [6]: x = MyClass() In [7]: x.i Out[7]: 100 Copy code
Above, we created an instance of MyClass and assigned it to x.
By accessing the i value in x, we can find that the i value is consistent with the i value in the MyClass variable.
The instantiation operation (call class object) creates an empty object. If you want to do some custom operations when instantiating, you can define one in the class__ init__ () method, the instantiation operation of the class will automatically initiate a call for the newly created class instance__ init__ ().
def __init__(self): self.data = [] Copy code
__ init__ () method can also accept parameters. These parameters are passed in when we instantiate the class:
>>> class Complex: ... def __init__(self, realpart, imagpart): ... self.r = realpart ... self.i = imagpart ... >>> x = Complex(3.0, -4.5) >>> x.r, x.i (3.0, -4.5) Copy code
Properties of the instance object
As for the above class, we have defined an i attribute and an f method:
class MyClass: """A simple example class""" i = 12345 def f(self): return 'hello world' Copy code
We can access this property through the instance object:
In [6]: x = MyClass() In [7]: x.i Out[7]: 100 Copy code
We can even create a property in the instance object that does not belong to the class object:
In [8]: x.y=200 In [9]: x.y Out[9]: 200 Copy code
Do not keep any records even after use:
x.counter = 1 while x.counter < 10: x.counter = x.counter * 2 print(x.counter) del x.counter Copy code
Method object
There are two ways to access the methods defined in the function, one through class objects and the other through instance objects. See the differences between the two:
In [10]: x.f Out[10]: <bound method MyClass.f of <__main__.MyClass object at 0x7fb69fc5f438>> In [11]: x.f() Out[11]: 'hello world' In [12]: MyClass.f Out[12]: <function __main__.MyClass.f> In [13]: MyClass.f() --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-13-e50d25278077> in <module>() ----> 1 MyClass.f() TypeError: f() missing 1 required positional argument: 'self' Copy code
From the above output, we can see that MyClass F is a function and x.f is an object object object.
Remember the definition of the F method? The F method has a self parameter. If you call it as a function, you must pass in all the required parameters, which is why you directly call MyClass The reason why f () reports an error and x.f() can run directly.
Although the first parameter of a method is often named self. This is just a convention: the name self has absolutely no special meaning in Python.
Method object is special in that the instance object is passed in as the first parameter of the function. In our example, calling x.f() is actually equivalent to MyClass.. f(x). In short, calling a method with n parameters is equivalent to calling the corresponding function of another parameter. The parameter value is the instance object to which the method belongs, and the position is before other parameters.
Why doesn't the method object need to pass in the parameter self? From the output of x.f, we can see that this method has been bound to an instance object, so the self parameter will be automatically passed in.
Method can call other methods by using the method attribute of the self parameter:
class Bag: def __init__(self): self.data = [] def add(self, x): self.data.append(x) def addtwice(self, x): self.add(x) self.add(x) Copy code
Class variables and instance variables
What problems should we pay attention to in the use of class variables and instance variables?
In general, instance variables are used for the unique data of each instance, while class variables are used for the properties and methods shared by all instances of the class.
class Dog: kind = 'canine' # class variable shared by all instances def __init__(self, name): self.name = name # instance variable unique to each instance >>> d = Dog('Fido') >>> e = Dog('Buddy') >>> d.kind # shared by all dogs 'canine' >>> e.kind # shared by all dogs 'canine' >>> d.name # unique to d 'Fido' >>> e.name # unique to e 'Buddy' Copy code
Therefore, if it is an instance variable, it needs to be assigned and initialized in the initialization method. If it is a class variable, it can be defined directly in the structure of the class.
An example of using instance variables correctly:
class Dog: def __init__(self, name): self.name = name self.tricks = [] # creates a new empty list for each dog def add_trick(self, trick): self.tricks.append(trick) >>> d = Dog('Fido') >>> e = Dog('Buddy') >>> d.add_trick('roll over') >>> e.add_trick('play dead') >>> d.tricks ['roll over'] >>> e.tricks ['play dead'] Copy code
If the same attribute name appears in both instances and classes, the attribute lookup will give priority to the instance:
>>> class Warehouse: purpose = 'storage' region = 'west' >>> w1 = Warehouse() >>> print(w1.purpose, w1.region) storage west >>> w2 = Warehouse() >>> w2.region = 'east' >>> print(w2.purpose, w2.region) storage east Copy code
inherit
Look at the inherited syntax in Python:
class DerivedClassName(BaseClassName): <statement-1> . . . <statement-N> Copy code
If the base class is defined in another module:
class DerivedClassName(modname.BaseClassName): Copy code
If the requested property is not found in the class, the search will go to the base class. If the base class itself is derived from some other class, this rule is applied recursively.
A derived class may override the methods of its base class. Because methods do not have special permissions when calling other methods of the same object, a base class method that calls another method defined in the same base class may eventually call a method that overrides its derived class.
There are two built-in functions in Python that can be used to easily judge whether to inherit or instance:
- Use isinstance() to check the type of an instance: For example, isinstance(obj, int) is only available in obj__ class__ True when is int or a class derived from int.
- Use issubclass() to check the inheritance relationship of the class: For example: issubclass(bool, int) is True because bool is a subclass of int. However, issubclass(float, int) is False because float is not a subclass of int.
Python also supports multiple inheritance:
class DerivedClassName(Base1, Base2, Base3): <statement-1> . . . <statement-N> Copy code
If a property is not found in DerivedClassName, it will be searched in Base1, and then (recursively) in Base1's base class. If it is not found there, it will be searched in Base2, and so on.
private variable
Although there is no mandatory syntax for private variables in Python, most Python code follows the convention that names with an underscore (such as _spam) should be treated as non-public parts of the API (whether it is a function, method, or data member).
This is only an implementation detail when we write Python programs, not a mandatory specification of syntax.
Since there are private variables, private variables may be overwritten in the case of inheritance. How does Python solve it?
In Python, you can avoid overwriting private variables by rewriting variable names.
Any form is__ The text of the spam identifier (with at least two prefix underscores and at most one suffix underscores) will be replaced with_ classname__spam, where classname is the name of the current class without the prefix underscore. This rewriting does not consider the syntactic position of the identifier, as long as it appears inside the class definition.
for instance:
class Mapping: def __init__(self, iterable): self.items_list = [] self.__update(iterable) def update(self, iterable): for item in iterable: self.items_list.append(item) __update = update # private copy of original update() method class MappingSubclass(Mapping): def update(self, keys, values): # provides new signature for update() # but does not break __init__() for item in zip(keys, values): self.items_list.append(item) Copy code
The above example even introduces one in mappingsubclass__ There is no error in the case of update identifier, because it will be replaced with in the mapping class_ Mapping__update is replaced with in the mappingsubclass class_ MappingSubclass__update.
Please note that the code passed to exec() or eval() will not treat the class name of the calling class as the current class; This is similar to the effect of a global statement, so this effect is limited to code compiled by bytecode at the same time.
iterator
For most container objects, you can use the for statement to traverse the elements in the container.
for element in [1, 2, 3]: print(element) for element in (1, 2, 3): print(element) for key in {'one':1, 'two':2}: print(key) for char in "123": print(char) for line in open("myfile.txt"): print(line, end='') Copy code
The underlying principle is that the for statement will call the iter() method on the container object. This function returns a defined__ next__ () method, which will access the elements in the container one by one. When the elements are exhausted__ next__ () will throw a StopIteration exception to notify termination of the for loop.
You can use the next() built-in function to call__ next__ () method; The following example shows how to use:
>>> s = 'abc' >>> it = iter(s) >>> it <iterator object at 0x00A1DB50> >>> next(it) 'a' >>> next(it) 'b' >>> next(it) 'c' >>> next(it) Traceback (most recent call last): File "<stdin>", line 1, in <module> next(it) StopIteration Copy code
After knowing the principle of iterators, we can add iterator objects for custom classes. We need to define one__ iter__ () method to return a__ next__ () object of the method. If the class is already defined__ next__ (), then__ iter__ () can simply return self:
class Reverse: """Iterator for looping over a sequence backwards.""" def __init__(self, data): self.data = data self.index = len(data) def __iter__(self): return self def __next__(self): if self.index == 0: raise StopIteration self.index = self.index - 1 return self.data[self.index] Copy code
generator
The generator is a simple and powerful tool for creating iterators. They are written like standard functions, but they use the yield statement when they want to return data. Every time next() is called on the generator, it resumes execution from where it left last time (it remembers all the data values when the statement was last executed).
Take an example of a generator:
def reverse(data): for index in range(len(data)-1, -1, -1): yield data[index] >>> >>> for char in reverse('golf'): ... print(char) ... f l o g Copy code
The operations that can be done with the generator can also be done with the class based iterator described in the previous section. But the generator is more compact because it is created automatically__ iter__ () and__ next__ () method.
The generator can also be executed in the form of expression code, which is similar to the list derivation, but the outer layer is parentheses instead of square brackets.
>>> sum(i*i for i in range(10)) # sum of squares 285 >>> xvec = [10, 20, 30] >>> yvec = [7, 5, 3] >>> sum(x*y for x,y in zip(xvec, yvec)) # dot product 260 >>> unique_words = set(word for line in page for word in line.split()) >>> valedictorian = max((student.gpa, student.name) for student in graduates) >>> data = 'golf' >>> list(data[i] for i in range(len(data)-1, -1, -1)) ['f', 'l', 'o', 'g']