Python learning road 8-class

This series is a compilation of notes for the introductory book "Python Programming: From Getting Started to Practice", which belongs to the primary content. The title sequence follows the title of the book.
This chapter mainly introduces an important programming idea: object-oriented programming, including concepts and operations such as classes and objects.

Overview

Object-oriented programming (OOP) is one of the most effective ways to write software. Object-oriented thinking is also the way humans have known the world since ancient times, that is, "classification". In the past experience, the object-oriented idea that I am most impressed with is the classification of nature in middle school biology textbooks: Jiemen gangmu, genus and species. There are two concepts to understand here: class and object. Class is a general abstract concept, a generalization of a group of similar things, a virtual concept, and these "things" are objects, for example: the concept of "dog", this is a "class", even if it is specific A specific species, such as a husky, is also a class. Only when it is really specific to a certain dog, such as "your husky A", does it reach the concept of "object". To sum up: a class is an abstraction Yes, the object is actual . The process from a class to an object is called instantiation of a class .

Create and use classes

Create a Car class

In Python, class names generally use camel case, that is, the first letter of each word is capitalized instead of underscores, and both instance names and module names are lowercase and spliced ​​with underscores. And, whether you are writing functions, classes, or code files, it is best to add a docstring, such as the triple-quote string below.

class Car:
    """一次模拟汽车的简单尝试"""

    def __init__(self, make, model, year):
        """初始化描述汽车的属性"""
        self.make = make
        self.model = model
        self.year = year
        self.odometer_reading = 0  # 里程表

    def get_descriptive_name(self):
        """返回整洁的描述性信息"""
        long_name = str(self.year) + " " + self.make + " " + self.model
        return long_name.title()

    def read_odometer(self):
        """打印一条指出汽车历程的消息"""
        print("This car has " + str(self.odometer_reading) + " miles on it.")

    def update_odometer(self, mileage):
        """将里程表读书设置为指定的值,且禁止读数回调"""
        if mileage <= 0:
            print("Mileage must be bigger than 0!")
        elif mileage >= self.odometer_reading:
            self.odometer_reading = mileage
        else:
            print("You can't roll back an odometer!")

    def increment_odometer(self, miles):
        """将里程表读数增加指定的量,且该量必须为正数"""
        if miles > 0:
            self.odometer_reading += miles
        else:
            print("Mile must be bigger than 0!")

    def fill_gas_tank(self):
        """将油箱装满"""
        print("The gas tank has been filled!")

Here are a few things to note:

① The functions in the class are called methods, such as the three functions defined above; selfthe variables bound to the class are called attributes, such as make, model, year(not referring to the three formal parameters , but the variablesself bound to them ).

②Every class must have a __init()__method, which is called a constructor (called a constructor in C++, but don't worry too much about whether it is a "method" or a "function", a thing is different in different places name only). Of course, it also has a default version, that is, there is only one selfparameter, and the function does nothing, which also means that you don't even need to define this method, and Python will automatically generate and call the default constructor, but "do not define the constructor. "It is estimated that this situation can only be encountered when the author is a beginner ^_^.

③The selfparameter in Python is the formal parameter that every non-static method in the class must have, and it must be placed first. It is a reference to the instance itself (not the class itself!), so that the instance can access the attributes in the class And method, we do not need to manually pass in the parameter when calling the method of the class, it will be passed in automatically. Attributes in a class can be accessed in all methods in the class, which is achieved through selfparameters. If you understand it from the perspective of C++, selfit is equivalent to a thispointer in a C++ class, pointing to the object itself.

④Each property in a class must have an initial value , even if the value is 0, an empty string or None. For example, for the four attributes in this example, the values ​​of the first three attributes are passed in by the user, and odometer_readingtheir values ​​are set to 0.

⑤ The class name in the first line of the above code Carmay or may not be followed by parentheses, that is, class Car:this way of writing is feasible, and class Car():this way of writing is also possible.

Use the Car class

The following code creates an object of class Car and performs simple operations on the object.

# 代码:
class Car:
    -- snip --     # 这不是一个Python语法!这里只是表示省略。

my_new_car = Car("audi", "a4", 2016)
print(my_new_car.get_descriptive_name())
my_new_car.read_odometer()

# 直接修改属性
my_new_car.odometer_reading = -100
my_new_car.read_odometer()
my_new_car.odometer_reading += -1
my_new_car.read_odometer()

# 通过方法修改属性
my_new_car.update_odometer(-100)
my_new_car.read_odometer()
my_new_car.increment_odometer(-1)
my_new_car.read_odometer()

my_new_car.update_odometer(100)
my_new_car.read_odometer()
my_new_car.increment_odometer(1)
my_new_car.read_odometer()

# 结果:
2016 Audi A4
This car has 0 miles on it.
This car has -100 miles on it.
This car has -101 miles on it.
Mileage must be bigger than 0!
This car has -101 miles on it.
Mile must be bigger than 0!
This car has -101 miles on it.
This car has 100 miles on it.
This car has 101 miles on it.

As can be seen from the above code, Python, like C++ and Java, also uses period notation to access attributes and call methods. As can be seen from the above code and results, the properties of the instance can be accessed and modified directly or through methods.

Directly accessing the properties of an object can make operations simple, but it violates the closure principle, and directly modifying properties is not conducive to standardizing operations on properties. For example, the mileage is set to a negative value in the code, and the increment is also a negative value when the mileage is increased, which is obviously unreasonable (although it can sometimes be done). And if you put operations on properties into methods, you can standardize these operations, such as the above read_odometer(), update_odometer(), increment_odometer()and other methods. And this is also the practice advocated by object-oriented programming, try not to expose properties directly to the outside world. Unfortunately, any kind of property in Python can be manipulated directly.

inherit

You don't always start from scratch when writing a class, if the class to be written is a special version of an existing class, i.e. has the same or similar properties and methods, a new class can be inherited (derived) from an existing class. Inherited classes are called "parent classes" , "base classes" or "superclasses" , and new classes are called "subclasses" or "derived classes" .

However, it should be noted that the inheritance relationship should only occur between classes with strong mutual relationships, such as the electric vehicle class derived from the car class, and the husky operation that is not derived from the car class.

Here is the code Carto derive the ElectricCarclass from the class:

# 代码:
class Car:
    -- snip --

class ElectricCar(Car):
    """电动汽车的独特之处"""

    def __init__(self, make, model, year):
        """初始化父类的属性,再初始化电动汽车特有的属性"""
        super().__init__(make, model, year)
        self.battery_size = 70

    def describe_battery(self):
        """打印一条描述电池容量的消息"""
        print("This car has a " + str(self.battery_size) + "-kWh battery.")

    def fill_gas_tank(self):   # 重写了父类的方法
        """电动车没有油箱"""
        print("This car doesn't need a gas tank!")


my_audi = Car("audi", "a4", 2018)
print(my_audi.get_descriptive_name())
my_audi.fill_gas_tank()
print()     # 用作空行

my_tesla = ElectricCar("tesla", "model s", 2018)
print(my_tesla.get_descriptive_name())
my_tesla.describe_battery()
my_tesla.fill_gas_tank()

# 结果:
2018 Audi A4
The gas tank has been filled!

2018 Tesla Model S
This car has a 70-kWh battery.
This car doesn't need a gas tank!

A few points can be concluded from the above code:

①When creating an instance of a subclass, Python first needs to initialize the parent class, super()return the reference of the parent class through the function, and then call the constructor of the parent class, ie super().__init__(参数列表). In Python2, the initialization of the parent class needs to initialize the parent class in the following way:

super(ElectricCar, self).__init__(make, model, year)

In Python3, the parent class can also be initialized in the above way, but the parameters in the function can also be omitted in single inheritance .super()

②The subclass can access all the properties of the parent class, and can also add new properties: my_teslathe object accesses the properties of the parent class, make, model, yearetc., and also adds battery_sizeproperties.

③ Subclass can override the method of the parent class: The ElectricCarclass overrides the method of the Carclass fill_gas_tank().

Two concepts need to be distinguished here: Override and Overload

Overriding, also called overriding, is mainly used in inheritance. When the class in the inheritance relationship has the same method, but the subclass and the superclass have different operations in this method, the subclass rewrites the method to override the method inherited from the superclass. When calling, Python will automatically determine whether the object is a derived class to call the corresponding implementation of the method. It is with rewriting that the feature of polymorphism in object orientation is realized.

Overloading is mainly used for functions (methods). In languages ​​like C/C++, Java, there can be multiple functions with the same name, but the parameter lists must be different, such as the number of parameters and the parameter types. These languages ​​distinguish which one of the functions of the same name is called based on the parameter list. But overloading is not polymorphism! When these languages ​​compile source files, they will generate different function names for the functions of the same name according to the parameter list (the specific method is to add a prefix or suffix), and then replace these functions of the same name in the source code with new function names. Loading is not polymorphic. But there is no such thing as function overloading in Python! Because Python has the artifact of keyword parameters and variable parameters (of course, C++ also has variable-length parameters, which are represented by three dots. I don't know if the underlying implementation of Python's variable parameters is related to C++'s variable-length parameters).

But it doesn't matter! It is enough to understand the concepts of rewriting and overloading, and it is enough to know how to use them. It is not important whether these two are related to polymorphism. So far, there is no accurate statement on the relationship between these two and polymorphism on the Internet. When I read C++ books before, I remembered to specifically mention the underlying implementation of overloading (which book I forgot), but the author is ignorant, and I don’t know what happens when rewriting is compiled. Maybe it is also by generating new Function name and replace, if so, overloading can also be considered polymorphic, but this is just the author's guess! Interested partners can study the situation of these two at compile time.

The reason why these two are brought up separately is that many people have loaded this concept in their postgraduate entrance exams or job interviews. Especially in the postgraduate entrance examination, the postgraduate re-examination seems to be more inclined to rewrite polymorphism, and overloading is not polymorphic.

use instance as property

When using code to simulate physical objects, as development progresses, there will inevitably be more and more properties and methods of a class, and the code of a single class will become longer and longer. At this time, you can consider whether a part of the code can be extracted separately as a new class. For example ElectricCar, the battery in the previous class can be presented separately as a class.

# 代码:
class Car:
    -- snip --

class Battery:
    """一次模拟电动汽车电池的简单尝试"""

    def __init__(self, battery_size=70):
        """初始化电池的属性"""
        self.battery_size = battery_size

    def describe_battery(self):
        """打印一条描述电池容量的信息"""
        print("This car has a " + str(self.battery_size) + "-kWh battery.")

    def get_range(self):
        """输出电池的续航里程"""
        if self.battery_size == 70:
            miles = 240
        elif self.battery_size == 85:
            miles = 270

        message = "This car can go approximately " + str(miles) + " miles on a full charge."
        print(message)

class ElectricCar(Car):
    def __init__(self, make, model, year):
        super().__init__(make, model, year)
        self.battery = Battery()

my_tesla = ElectricCar("tesla", "model s", 2018)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()
my_tesla.battery.get_range()

# 结果:
2018 Tesla Model S
This car has a 70-kWh battery.
This car can go approximately 240 miles on a full charge.

When simulating complex physical objects, some interesting questions need to be solved, such as whether the cruising range is the attribute of the battery or the attribute of the car? get_range()There's nothing wrong with putting the method in if you're only describing one car Battery(), but what if you're describing an entire line of cars? For example, how far can this model run, it may be ElectricCarmore appropriate to put this method into a class. But in any case, what is emphasized here is that the problem should be considered at a higher logical level.

Import classes from modules

Similar to the last article about functions, classes can also form modules by themselves. A class can be a module, or multiple classes (usually related classes) can be put into a module. For example, put the above Carclasses in a separate file, remove the code of this class, delete other codes, and finally name the file car.py(note that the file name here is lowercase). Then bring this class into the program:

from car import Car
# 如果命名有冲突,也可以给Car类起个别名
# from car import Car as C

my_new_car = Car("audi", "a4", 2018)
print(my_new_car.get_descriptive_name())

my_new_car.odometer_reading = 23
my_new_car.read_odometer()

You can also put multiple related classes into the same file to form a module, such as the above Carclass, ElectricCarclass and Batteryclass, name the file cars.py, and finally import the file:

from cars import Car, ElectricCar

my_beetle = Car("volkswagen", "beetle", 2018)
my_tesla = ElectricCar("tesla", "model s", 2018)
-- snip --     # 后面的代码和之前的类似,不在赘述

It is also possible to import the entire module and use the classes in the module using period notation:

import cars

my_car = car.Car("volkswagen", "beetle", 2018)
my_tesla = car.ElectricCar("tesla", "model s", 2018)

You can also import all the classes in the module (this method is not recommended, it is easy to cause naming conflicts!), in which case you do not need to use the period notation.

from cars import *

my_beetle = Car("volkswagen", "beetle", 2018)

You can also import another module in a module, for example, put the Carclass in a file alone as a formal parameter to a module, name it car.py, create a new module electric_car.pyto store the Batteryclass and ElectricCarthe class, and bring the class into the module Car:

from car import Car

class Battery:
    -- snip --

class ElectricCar(Car):
    -- snip --

Finally import the classes as needed in the source code of the executable:

# 这是书中导入两个类的代码
from car import Car
from electric_car import ElectricCar     

my_car = Car("audi", "a4", 2018)
my_tesla = ElectricCar("tesla", "model s", 2018)

When I read this before, I wondered if I could import Cara class like this:

from electric_car import Car, ElectricCar

my_car = Car("audi", "a4", 2018)
my_tesla = ElectricCar("tesla", "model s", 2018)

After testing, it is possible to do so. Then the question comes, does the import method like in the book cause code coverage? Which import is more efficient? The author is still a little confused here, I will update it later.

There are many ways to import modules, and you can even import modules directly from GitHub. The above import methods are just scratches. The article ends with an example of importing OrderedDicta class from the standard library. In previous versions of Python, ordinary dictionary classes did not ensure the previous order of key-value pairs. To ensure the order, you had to use OrderedDictclasses. But now from version 3.6, Python also ensures that the key-value pairs in ordinary dictionaries are also ordered, but for compatibility reasons (it is possible that your code will run in versions before 3.6), it is still recommended to use OrderedDictclasses.

# 代码:
from collections import OrderedDict

favorite_languages = OrderedDict()

favorite_languages["jen"] = "python"
favorite_languages["sarah"] = "c"
favorite_languages["edward"] = "ruby"
favorite_languages["phil"] = "python"

for name, language in favorite_languages.items():
    print(name.title() + "'s favorite_language is " + language.title())

# 结果:
Jen's favorite_language is Python
Sarah's favorite_language is C
Edward's favorite_language is Ruby
Phil's favorite_language is Python

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325657955&siteId=291194637