class It's a very important concept in object-oriented programming ,python There are also class, All standard programming features are supported : Inherit , Polymorphism etc. .
This article will explain in detail Python in class Information about .
In the detailed explanation class Before , Let's look at the concept of scope and namespace .
Namespace (Namespace) It's a mapping from name to object , Most of the namespace is through Python Dictionary to achieve .
The main purpose of a namespace is to avoid name conflicts in a program . As long as the name remains unique in the same namespace , Names in different command spaces don't affect each other .
Python There are three kinds of namespace in :
The search order for the namespace is Local name -》 Global name -》 Built in name .
Namespaces created at different times have different lifetimes . The namespace containing the built-in name is in Python Created when the interpreter starts , It will never be deleted . The global namespace of a module is created when the module definition is read in .
Usually , The module namespace will also persist until the interpreter exits .
The statement executed by the top-level call of the interpreter , For example, a program that reads from a script file or interactively , Is considered to be __main__
Part of the module call , So they also have their own global namespace .( The built-in name actually exists in a module ; This module is called builtins .)
One Scope Is a namespace directly accessible Python The text area of the program .
Python There are four scopes in :
The search order for scopes is Local -> Enclosing -> Global -> Built-in
Python of use nonlocal The keyword is declared as Enclosing Range , use global Keyword declared as global scope .
Let's take a look at one global and nonlocal Examples of how variable binding can be affected :
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
The above program outputs :
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 default to local Scope , If you want to modify the variables of the external function in the function of the function , Then 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 the reference, you need to declare it as global Scope .
Python The class in is used to class To define the , Let's look at the simplest class Definition :
class ClassName: <statement-1> . . . <statement-N> Copy code
The code in the class definition creates a new namespace , The variables inside are considered local scopes . All assignments to local variables are in this new namespace .
class After defining the class , A class object is generated . We can use this class object to access the properties and methods defined in the class .
For example, we define the following class :
class MyClass: """A simple example class""" i = 12345 def f(self): return 'hello world' Copy code
Class defines a property i And a way f. Then we can pass MyClass.i and
MyClass.f Come and visit them .
Be careful ,Python There is no such thing as java Medium private,public This kind of variable access scope control . You can take Python class The variables and methods in are seen as public Of .
We can go directly to MyClass.i
Assign values to change i The value of the 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
Class in , We also defined class Documents , You can go directly through __doc__
To visit .
Instantiate a class object , You can think of a class as a parameterless function .
In [6]: x = MyClass() In [7]: x.i Out[7]: 100 Copy code
Above we created a MyClass Example , And assigned to x.
By visiting x Medium i value , We can find this i The value is and MyClass In class variables i The values are consistent .
Instantiation operation (“ call ” Class object ) Will create an empty object . If you want to do some custom operations when instantiating , Then you can define a __init__()
When the method is used , 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__()
Methods can also take 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
Or above class, We define a i Properties and a f Method :
class MyClass: """A simple example class""" i = 12345 def f(self): return 'hello world' Copy code
We can access this property through instance objects :
In [6]: x = MyClass() In [7]: x.i Out[7]: 100 Copy code
We can even create a property in an instance object that does not belong to a class object :
In [8]: x.y=200 In [9]: x.y Out[9]: 200 Copy code
Even after use , Don't keep any records :
x.counter = 1 while x.counter < 10: x.counter = x.counter * 2 print(x.counter) del x.counter Copy code
We have two ways to access methods defined in functions , One is through class objects , One is through instance objects , Look at the difference 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 output above, we can see that ,MyClass.f It's a function , and x.f It's a object object .
Remember f Definition of method ?f There is one method self Parameters , If called as a function , You must pass in all the required parameters , That's why it's called directly MyClass.f() Report errors , and x.f() The reason why it can run directly .
Although the first parameter of a method is often named self
. This is just an agreement : self
The name is in Python There is absolutely no special meaning in .
The special thing about the first method that is passed as an object parameter is that it's called an object . In our example , call x.f()
It's the same thing as MyClass.f(x)
. All in all , Call a with n A method with one parameter is equivalent to calling the corresponding function with another parameter , The value of this parameter is the instance object to which the method belongs , Position before other parameters .
Why method objects don't need to be passed in self What about this parameter ? from x.f We can see that , This method is already bound to an instance object , therefore self Parameters are automatically passed in .
Method can be used by self
Parameter to call other methods :
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
In the use of class variables and instance variables , What problems should we pay attention to ?
Generally speaking , Instance variables are used for unique data for each instance , Class variables are used for properties and methods shared by all instances of a 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's an instance variable , Then you need to assign and initialize in the initialization method . If it's a class variable , It can be defined directly in the structure of a 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 property name appears in both instances and classes , Then the attribute lookup takes precedence over 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
look down Python The inherited syntax in :
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 cannot be found in the class , The search will go to the base class to find . If the base class itself is derived from some other class , Then this rule will be applied recursively .
Derived classes may override the methods of their base classes . Because methods do not have special permissions when calling other methods of the same object , So a base class method that calls another method defined in the same base class may eventually call a method that covers its derived class .
Python There are two built-in functions that can be used to easily determine whether it is an inheritance or an instance :
__class__
by int Or one derived from int The class time of is True. Python Multiple inheritance is also supported :
class DerivedClassName(Base1, Base2, Base3): <statement-1> . . . <statement-N> Copy code
If a property is in DerivedClassName
No , Then it will arrive Base1
Search for it , then ( recursively ) To Base1
Search for , If you can't find it there , Until then Base2
Mid search , And so on .
although Python There is no mandatory syntax for private variables in , But most Python The code follows such a convention : Name with an underscore ( for example _spam
) It should be taken as API The non-public part of ( Whether it's a function 、 Method or data member ).
It's just that we're writing Python An implementation detail of the program , It's not a mandatory specification of grammar .
Since there are private variables , Then in the case of inheritance, it is possible to have private variable coverage ,Python How to solve it ?
Python You can avoid the private variable's coverage by rewriting the variable name .
In any form __spam
Identifier ( Underline with at least two prefixes , At most one suffix underline ) The text of will be replaced with _classname__spam
, among classname
For the current class name without prefix underscores . This rewriting does not take into account the syntactic position of the identifier , As long as it appears inside the class definition, it will do .
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 in MappingSubclass
Introduced a __update
There is no error in the case of identifiers , Because it will be in Mapping
Class is replaced with _Mapping__update
And in the MappingSubclass
Class is replaced with _MappingSubclass__update
.
Please pay attention to pass it on to exec()
or eval()
The code will not consider the class name of the calling class as the current class ; This is similar to global
The effect of the statement , So this effect is limited to code that is compiled in bytecode at the same time .
For most container objects , have access to 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 for Statement is called on the container object iter() Method . This function returns a definition __next__()
Iterator object for method , This method will access the elements in the container one by one . When the elements are exhausted ,__next__()
Will lead to StopIteration Exception to notify termination for loop .
You can use next() Built in function to call __next__()
Method ; The following example shows how to use it :
>>> 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 customize it for class Add iterator object , We need to define one __iter__()
Method to return a __next__()
Object of method . If the class already defines __next__()
, be __iter__()
You can simply go back to 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 Is a simple and powerful tool for creating iterators . They are written like standard functions , But when they want to return data, they use yield sentence . Every time you call on the generator next() when , It will resume execution from where it left last time ( It remembers all the data values from the last statement execution ).
Look at 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
What can be done with generators can also be done with the class based iterators described in the previous section . But the generator is more compact , Because it will automatically create __iter__()
and __next__()
Method .
The generator can also be executed in the form of expression code , This is similar to the list derivation , But the outer layer is parentheses, not 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']