Fundamentals of Python: classes in Python

Programmer Xingang 2022-06-23 17:50:02 阅读数:872

fundamentalspythonclassespython

brief introduction

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 .

Scope and namespace

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 :

  • Built in name (built-in names), Python Language built-in names , Like the function name abs、char And exception name BaseException、Exception wait .
  • Global name (global names), The name defined in the module , Module variables recorded , Include function 、 class 、 Other imported modules 、 Module level variables and constants .
  • Local name (local names), The name defined in the function , The variables of the function are recorded , Including parameters of functions and locally defined variables .( Class is also defined 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 :

  • Local: The innermost layer , Contains local variables , Like a function / Methods the internal .
  • Enclosing: Contains nonlocal (non-local) It's not the whole picture (non-global) The variable of . Like two nested functions , A function ( Or class ) A It contains a function B , So for B For the name of A The scope in is nonlocal.
  • Global: The outermost layer of the current script , For example, the global variables of the current module .
  • Built-in: Contains built-in variables / Keywords, etc ., It was finally searched

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 .

class

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 object

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 .

Class

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 

Properties of an instance object

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 

Method object

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 

Class variables and instance variables

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 

Inherit

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 :

  • Use isinstance() To check the type of an instance : for example :isinstance(obj, int) Only in the obj.__class__ by int Or one derived from int The class time of is True.
  • Use issubclass() To check the inheritance relationship of the class : for example : issubclass(bool, int) by True, because bool yes int Subclasses of . however ,issubclass(float, int) by False, because float No int Subclasses of .

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 .

Private variables

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 .

iterator

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

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']
版权声明:本文为[Programmer Xingang]所创,转载请带上原文链接,感谢。 https://pythonmana.com/2022/01/202201051804123027.html