《Python从入门到实践》第九章 类

面向对象编程是最有效的软件编写方法之一

在面向对象编程时,你编写表示现实世界中的事物和情景的,并基于这些类来创建对象

根据类来创建对象称为实例化,这让你能够使用类的实例

创建和使用类

创建Dog类

class Dog:
    """一次模拟小狗的简单尝试"""

    def __init__(self, name, age):
        """初始化属性 name 和 age"""
        self.name = name
        self.age = age

    def sit(self):
        """模拟小狗收到命令时坐下"""
        print(f"{self.name} is now sitting.")

    def roll_over(self):
        print(f"{self.name} rolled over!")

在Python中,首字母大写的名称指的是类,因为这是我们创建的全新的类,所以定义时不加括号,然后是一个文档字符串,对这个类的功能做了描述

__init__() 方法

类中的函数称为方法,前面学到的有关函数的一切都适用于方法,就目前而言,唯一重要的差别是调用方法的方式。

__init__()是一个特殊方法,每当你根据Dog类创建新实例时,Python都会自动运行它,在这个方法的名称中,前后各有两个下划线,这是一种约定,是为了避免Python默认方法与普通方法发生名称冲突。

我们将__init__()方法定义成包括三个形参:self、name、age。在这个方法的定义当中,self必不可少,而且必须位于其他形参的前面,因为当Python调用这个方法来创建Dog实例时,将自然传入实参self,该实参是一个指向实例本身的引用,让实例能够访问类中的属性和方法,当我们根据Dog类创建实例时,都只需给最后两个形参(name和age提供值)。

在__init__()方法内定义的两个变量都有前缀self,以self为前缀的变量可供类中的所有方法使用,可以通过类的任意实例来访问,self.name=name获取与形参name相关联的值,并将其赋给变量name,然后该变量被关联到当前创建的实例。

self.age = age的作用与此类似,想这样可通过实例访问的变量称为属性。

Dog类还定义了另外两个方法:sit()和roll_over()。由于这些方法的执行不需要额外的信息,因此只有一个形参self.

根据类创建实例:

class Dog:
    """一次模拟小狗的简单尝试"""

    def __init__(self, name, age):
        """初始化属性 name 和 age"""
        self.name = name
        self.age = age

    def sit(self):
        """模拟小狗收到命令时坐下"""
        print(f"{self.name} is now sitting.")

    def roll_over(self):
        print(f"{self.name} rolled over!")


my_dog = Dog('rich', 8)
print(f"My dog's name is {my_dog.name}")
print(f"My dog is {my_dog.age} years old")

访问属性:

my_dog.name

调用方法

my_dog = Dog('rich', 8)
my_dog.sit()
my_dog.roll_over()

练习9.1:

class Restaurant:
    def __init__(self,restaurant_name,cuisine_type):
        self.restaurant_name = restaurant_name
        self.cuisine_type = cuisine_type

    def describe_restaurant(self):
        print(f"restaurant's name is {self.restaurant_name}")
        print(f"cuisine_type is {self.cuisine_type}")

    def open_restaurant(self):
        print(f"{self.restaurant_name.title()} is opening")


restaurant = Restaurant('dongbei BBQ', 'dun')
restaurant.describe_restaurant()
restaurant.open_restaurant()

练习9.3

class User:
    def __init__(self, first_name, last_name, age, phone_number):
        self.first_name = first_name
        self.last_name = last_name
        self.age = age
        self.phone_number = phone_number
        self.username = f"{self.first_name} {self.last_name}"

    def describe_user(self):
        print(f"user's name is {self.username}")
        print(f"{self.username} is {self.age} years old")
        print(f"{self.username}'s phone number is {self.phone_number}")

    def greet_user(self):
        print(f"Hello, {self.username}")


user1 = User('xiong', 'jiajin', 13, 110)
user1.describe_user()
user1.greet_user()

注意:一个类中所有的变量都要在__init__()中定义并完成初始化

使用类和实例

类编写好后,你的大部分时间将花在使用根据类创建的实例上,你需要完成的首要任务之一是,修改实例的属性。

既可以直接修改实例的属性,也可以编写方法以特定的方式进行修改。

class Car:
    """一次模拟汽车的简单尝试"""
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year

    def get_descriptive_name(self):
        """返回格式规范的描述性信息"""
        long_name = f"{self.year} {self.make} {self.model}"
        return long_name


my_new_car = Car('audi', 'A6', 2023)
print(my_new_car.get_descriptive_name())

给属性指定默认值

有些属性无须通过形参来定义,可以在__init__()方法中为其指定默认值,如下

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 = f"{self.year} {self.make} {self.model}"
        return long_name

    def read_odometer(self):
        """打印一条指出汽车行驶里程的消息"""
        print(f"This car has {self.odometer_reading} miles on it")


my_new_car = Car('audi', 'A6', 2023)
print(my_new_car.get_descriptive_name())
my_new_car.read_odometer()

但是实际上出售时里程读表为0的汽车不多,因此需要修改改属性。

修改属性的值

可以用三种不同的方式修改属性的值:直接通过实例修改,通过方法设置,以及通过方法递增(增加特定的值)。下面一次介绍这些方式

①直接修改属性的值

要修改属性里的值,最简单方式是通过实例直接访问它

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 = f"{self.year} {self.make} {self.model}"
        return long_name

    def read_odometer(self):
        """打印一条指出汽车行驶里程的消息"""
        print(f"This car has {self.odometer_reading} miles on it")


my_new_car = Car('audi', 'A6', 2023)
print(my_new_car.get_descriptive_name())

my_new_car.odometer_reading = 23
my_new_car.read_odometer()

②通过方法修改属性的值

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 = f"{self.year} {self.make} {self.model}"
        return long_name

    def read_odometer(self):
        """打印一条指出汽车行驶里程的消息"""
        print(f"This car has {self.odometer_reading} miles on it")

    def update_odometer(self, mileage):
        """将里程表设置为指定的数"""
        self.odometer_reading = mileage


my_new_car = Car('audi', 'A6', 2023)
print(my_new_car.get_descriptive_name())

my_new_car.update_odometer(26)
my_new_car.read_odometer()

还可以对update_odometer()方法进行扩展,使其在修改里程表读书时做一些额外的工作,比如添加一些逻辑,禁止里程表读数往回调整:

def update_odometer(self, mileage):
    """将里程表设置为指定的数"""
    self.odometer_reading = mileage
    if mileage >= self.odometer_reading:
        self.odometer_reading = mileage
    else:
        print("You can't roll back an odometer!")

③通过方法让属性的值递增

有时候需要将属性值递增特定的量,而不是将其设置为全新的值。

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 = f"{self.year} {self.make} {self.model}"
        return long_name

    def read_odometer(self):
        """打印一条指出汽车行驶里程的消息"""
        print(f"This car has {self.odometer_reading} miles on it")

    def update_odometer(self, mileage):
        """将里程表设置为指定的数"""
        self.odometer_reading = mileage
        if mileage >= self.odometer_reading:
            self.odometer_reading = mileage
        else:
            print("You can't roll back an odometer!")

    def increment_odometer(self, miles):
        """让里程表读数增加指定的值"""
        self.odometer_reading += miles


my_new_car = Car('audi', 'A6', 2023)
print(my_new_car.get_descriptive_name())

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

练习9.4:

class Restaurant:
    def __init__(self, restaurant_name, cuisine_type):
        self.restaurant_name = restaurant_name
        self.cuisine_type = cuisine_type
        self.number_served = 0

    def describe_restaurant(self):
        print(f"restaurant's name is {self.restaurant_name}")
        print(f"cuisine_type is {self.cuisine_type}")

    def open_restaurant(self):
        print(f"{self.restaurant_name.title()} is opening")

    def set_number_served(self, number_served_set):
        if number_served_set >= self.number_served:
            self.number_served = number_served_set
        else:
            print("you enter a wrong statistic")

    def increment_number_served(self, increment_number):
        if increment_number >= 0:
            self.number_served += increment_number
        else:
            print("the number input is less than zero")


restaurant = Restaurant('dongbei BBQ', 'dun')
restaurant.describe_restaurant()
restaurant.open_restaurant()
restaurant.number_served = 136
print(f"{restaurant.number_served} people have been served in {restaurant.restaurant_name}")
restaurant.set_number_served(147)
print(f"{restaurant.number_served} people have been served in {restaurant.restaurant_name}")
restaurant.increment_number_served(9)
print(f"{restaurant.number_served} people have been served in {restaurant.restaurant_name}")

练习9.5:

class User:
    def __init__(self, first_name, last_name, age, phone_number):
        self.first_name = first_name
        self.last_name = last_name
        self.age = age
        self.phone_number = phone_number
        self.username = f"{self.first_name} {self.last_name}"
        self.log_attempts = 0

    def describe_user(self):
        print(f"user's name is {self.username}")
        print(f"{self.username} is {self.age} years old")
        print(f"{self.username}'s phone number is {self.phone_number}")

    def greet_user(self):
        print(f"Hello, {self.username}")

    def increment_login_attempts(self):
        self.log_attempts += 1

    def reset_login_attempts(self):
        self.log_attempts = 0


user1 = User('xiong', 'jiajin', 13, 110)
user1.describe_user()
user1.greet_user()
user1.increment_login_attempts()
user1.increment_login_attempts()
print(user1.log_attempts)
user1.reset_login_attempts()
print(user1.log_attempts)

继承:

在编写类时,并非总要从头开始,如果编写的类是一个既有的类的特殊版本,可以使用继承。当一个类继承另一个类。当一个类继承另一个类时,将自动获得后者的所有属性和方法。原有的类称为父类,而新类称为子类。子类不仅继承父类的所有属性和方法,还可以定义自己的属性和方法。

①子类__init__()方法

在既有的类的基础上编写新类,通常要调用父类的__init__()方法。这将初始化在父类的__init__()方法中定义的所有属性,从而让子类也可以使用这些属性。

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 = f"{self.year} {self.make} {self.model}"
        return long_name

    def read_odometer(self):
        """打印一条指出汽车行驶里程的消息"""
        print(f"This car has {self.odometer_reading} miles on it")

    def update_odometer(self, mileage):
        """将里程表设置为指定的数"""
        self.odometer_reading = mileage
        if mileage >= self.odometer_reading:
            self.odometer_reading = mileage
        else:
            print("You can't roll back an odometer!")

    def increment_odometer(self, miles):
        """让里程表读数增加指定的值"""
        self.odometer_reading += miles


class ElectricCar(Car):
    """电动汽车的独到之处"""
    def __int__(self, make ,model, year):
        """初始化父类的属性"""
        super().__init__(make, model, year)


my_leaf = ElectricCar('nissan', 'leaf', 2023)
print(my_leaf.get_descriptive_name())

在创建子类时,父类必须包含在当前文件中,并且位于子类的前面,在定义子类时,必须在括号内指定父类的名称,__init__()方法接受创建Car实例所需的信息。

super()是一个特殊的函数,让你能够调用父类的方法,从而让ElectricCar实例包含这个方法定义的所有属性,父类也称为超类,函数名super由此得名。

给子类定义属性和方法

让一个类继承另一个类后,就可以添加区分子类和父类所需的新属性和新方法

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 = f"{self.year} {self.make} {self.model}"
        return long_name

    def read_odometer(self):
        """打印一条指出汽车行驶里程的消息"""
        print(f"This car has {self.odometer_reading} miles on it")

    def update_odometer(self, mileage):
        """将里程表设置为指定的数"""
        self.odometer_reading = mileage
        if mileage >= self.odometer_reading:
            self.odometer_reading = mileage
        else:
            print("You can't roll back an odometer!")

    def increment_odometer(self, miles):
        """让里程表读数增加指定的值"""
        self.odometer_reading += miles


class ElectricCar(Car):
    """电动汽车的独到之处"""
    def __int__(self, make, model, year):
        """
        先初始化父类的属性,在初始化电动车特有的属性
        """
        super().__init__(make, model, year)
        self.battery_size = 20

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


my_leaf = ElectricCar('nissan', 'leaf', 2023)
print(my_leaf.get_descriptive_name())
my_leaf.battery_size = 50
my_leaf.describe_battery()

重写父类中的方法:

再使用子类模拟实物的行为时,如果父类中的一些方法不能满足子类的需求,就可以用下面的办法重写:再子类中定义一个与要重写的父类方法同名的方法,这样Python将忽略这个父类方法,之关注你在子类中定义的相应方法。

将实例用作属性

在使用代码模拟实物时,你可能会发现自己给类添加了太多细节:属性和方法越来越多,文件越来越长。在这种情况下,可能需要将类的一部分提取出来,作为一个独立的类。将大型类拆分成多个协同工作的小类,这种方法称为组合。

一定要注意区分__init__()  和  __int__(),并且两边都是有两个下划线

例如:

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 = f"{self.year} {self.make} {self.model}"
        return long_name

    def read_odometer(self):
        """打印一条指出汽车行驶里程的消息"""
        print(f"This car has {self.odometer_reading} miles on it")

    def update_odometer(self, mileage):
        """将里程表设置为指定的数"""
        self.odometer_reading = mileage
        if mileage >= self.odometer_reading:
            self.odometer_reading = mileage
        else:
            print("You can't roll back an odometer!")

    def increment_odometer(self, miles):
        """让里程表读数增加指定的值"""
        self.odometer_reading += miles


class Battery:
    """一次模拟电动汽车电池的简单尝试"""
    def __init__(self, battery_size=40):
        self.battery_size = battery_size

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


class ElectricCar(Car):
    """电动汽车的独到之处"""
    def __init__(self, make, model, year):
        """
        先初始化父类的属性,在初始化电动车特有的属性
        """
        super().__init__(make, model, year)
        self.battery = Battery()


my_leaf = ElectricCar('nissan', 'leaf', 2023)
print(my_leaf.get_descriptive_name())
my_leaf.battery.describe_battery()

我们定义了一个名为Battery的新类,它没有继承任何类,__init__()方法在self之外还有一个形参battery_size。这个形参是可选的:如果没有给它提供值,电池容量将被设置为40,describe_battery()方法也被移到了这个类中。

在ElectricCar类中,添加一个名为self.battery的属性,这行代码让Python创建一个新的Battery实例(因为没有指定容量,所以默认值为40),并将该实例赋给属性self.battery。每当__init__()方法被调用时,都将执行该操作,因此现在每个ElectricCar实例都包含一个自动创建的Battery实例。

这看似做了很多额外的工作,但是现在想多详细地描述电池都可以,且不会导致ElectricCar类混乱不堪。

下面再给Battery类添加一个方法,它更具电池容量报告汽车的续航里程:

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 = f"{self.year} {self.make} {self.model}"
        return long_name

    def read_odometer(self):
        """打印一条指出汽车行驶里程的消息"""
        print(f"This car has {self.odometer_reading} miles on it")

    def update_odometer(self, mileage):
        """将里程表设置为指定的数"""
        self.odometer_reading = mileage
        if mileage >= self.odometer_reading:
            self.odometer_reading = mileage
        else:
            print("You can't roll back an odometer!")

    def increment_odometer(self, miles):
        """让里程表读数增加指定的值"""
        self.odometer_reading += miles


class Battery:
    """一次模拟电动汽车电池的简单尝试"""
    def __init__(self, battery_size=40):
        self.battery_size = battery_size

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

    def get_range(self):
        """打印一条消息,指出电池的续航里程"""
        if self.battery_size == 40:
            range = 150
        elif self.battery_size ==65:
            range =225
        print(f"This car can go about {range} miles on a full charge")


class ElectricCar(Car):
    """电动汽车的独到之处"""
    def __init__(self, make, model, year):
        """
        先初始化父类的属性,在初始化电动车特有的属性
        """
        super().__init__(make, model, year)
        self.battery = Battery(65)


my_leaf = ElectricCar('nissan', 'leaf', 2023)
print(my_leaf.get_descriptive_name())
my_leaf.battery.describe_battery()
my_leaf.battery.get_range()

模拟实物:

对现实世界的建模方法没有对错之分,有些方法的效率更高,但要找出效率最高的表示法,需要一定的实践,只要代码能够像你希望的那样运行,就说明你已经做得很好了!

练习9.6

class Restaurant:
    def __init__(self,restaurant_name,cuisine_type):
        self.restaurant_name = restaurant_name
        self.cuisine_type = cuisine_type
        self.flavors = ['orange', 'milk', 'apple']

    def describe_restaurant(self):
        print(f"restaurant's name is {self.restaurant_name}")
        print(f"cuisine_type is {self.cuisine_type}")

    def open_restaurant(self):
        print(f"{self.restaurant_name.title()} is opening")

    def describe_flavor(self):
        for flavor in self.flavors:
            print(f"{self.restaurant_name} provide {flavor} ice-cream")


restaurant = Restaurant('dongbei BBQ', 'dun')
restaurant.describe_restaurant()
restaurant.open_restaurant()

IceCreamStand = Restaurant('蜜雪冰城', '冰淇淋店')
IceCreamStand.describe_flavor()

练习9.7

class User:
    def __init__(self, first_name, last_name, age, phone_number):
        self.first_name = first_name
        self.last_name = last_name
        self.age = age
        self.phone_number = phone_number
        self.username = f"{self.first_name} {self.last_name}"
        self.log_attempts = 0
        self.privileges = ['can add post', 'can delete post', 'can ban user']

    def describe_user(self):
        print(f"user's name is {self.username}")
        print(f"{self.username} is {self.age} years old")
        print(f"{self.username}'s phone number is {self.phone_number}")

    def greet_user(self):
        print(f"Hello, {self.username}")

    def increment_login_attempts(self):
        self.log_attempts += 1

    def reset_login_attempts(self):
        self.log_attempts = 0

    def show_privileges(self):
        for privilege in self.privileges:
            print(f"{self.username}'s privileges include {privilege}")


user1 = User('xiong', 'jiajin', 13, 110)
user1.show_privileges()

练习9.8

class User:
    def __init__(self, first_name, last_name, age, phone_number):
        self.first_name = first_name
        self.last_name = last_name
        self.age = age
        self.phone_number = phone_number
        self.username = f"{self.first_name} {self.last_name}"
        self.log_attempts = 0

    def describe_user(self):
        print(f"user's name is {self.username}")
        print(f"{self.username} is {self.age} years old")
        print(f"{self.username}'s phone number is {self.phone_number}")

    def greet_user(self):
        print(f"Hello, {self.username}")

    def increment_login_attempts(self):
        self.log_attempts += 1

    def reset_login_attempts(self):
        self.log_attempts = 0


class Privileges:
    def __init__(self):
        self.privileges = ['can add post', 'can delete post', 'can ban user']

    def show_privileges(self):
        for privilege in self.privileges:
            print(f"manager's privileges include {privilege}")


class Admin(User):
    def __init__(self, first_name, last_name, age, phone_number):
        super().__init__(first_name, last_name, age, phone_number)
        self.privilege = Privileges()


user1 = Admin('xiong', 'jiajin', 13, 110)
user1.privilege.show_privileges()

super().__init__()后面不含有self

练习9.9

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 = f"{self.year} {self.make} {self.model}"
        return long_name

    def read_odometer(self):
        """打印一条指出汽车行驶里程的消息"""
        print(f"This car has {self.odometer_reading} miles on it")

    def update_odometer(self, mileage):
        """将里程表设置为指定的数"""
        self.odometer_reading = mileage
        if mileage >= self.odometer_reading:
            self.odometer_reading = mileage
        else:
            print("You can't roll back an odometer!")

    def increment_odometer(self, miles):
        """让里程表读数增加指定的值"""
        self.odometer_reading += miles



class Battery:
    """一次模拟电动汽车电池的简单尝试"""
    def __init__(self, battery_size=40):
        self.battery_size = battery_size

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

    def get_range(self):
        """打印一条消息,指出电池的续航里程"""
        if self.battery_size == 40:
            range = 150
        elif self.battery_size ==65:
            range =225
        print(f"This car can go about {range} miles on a full charge")

    def upgrade_battery(self):
        if self.battery_size != 65:
            self.battery_size = 65


class ElectricCar(Car):
    """电动汽车的独到之处"""
    def __init__(self, make, model, year):
        """
        先初始化父类的属性,在初始化电动车特有的属性
        """
        super().__init__(make, model, year)
        self.battery = Battery(40)


my_leaf = ElectricCar('nissan', 'leaf', 2023)
print(my_leaf.get_descriptive_name())
my_leaf.battery.describe_battery()
my_leaf.battery.get_range()
my_leaf.battery.upgrade_battery()
my_leaf.battery.get_range()

导入类:

①导入单个类

car.py

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 = f"{self.year} {self.make} {self.model}"
        return long_name

    def read_odometer(self):
        """打印一条指出汽车行驶里程的消息"""
        print(f"This car has {self.odometer_reading} miles on it")

    def update_odometer(self, mileage):
        """将里程表设置为指定的数"""
        self.odometer_reading = mileage
        if mileage >= self.odometer_reading:
            self.odometer_reading = mileage
        else:
            print("You can't roll back an odometer!")

    def increment_odometer(self, miles):
        """让里程表读数增加指定的值"""
        self.odometer_reading += miles

允许将类存储在模块中,然后再主程序中导入所需的模块

my_car.py

from car import Car

my_new_car = Car('audi', 'A6', '2024')
print(my_new_car.get_descriptive_name())

my_new_car.odometer_reading = 23
my_new_car.read_odometer()

从car.py中导入类Car,这让你的主程序文件变得整洁易读,还让你能够将大部分逻辑存储在独立的文件中。

②在一个模版中存储多个类

尽管同一个模块中的类之间应该存在某种相关性,但其实可以根据需要在一个模块中存储任意数量的类。Battery类和ElectricCar类都可帮助模拟汽车,下面将它们都加入模块car.py

car.py

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 = f"{self.year} {self.make} {self.model}"
        return long_name

    def read_odometer(self):
        """打印一条指出汽车行驶里程的消息"""
        print(f"This car has {self.odometer_reading} miles on it")

    def update_odometer(self, mileage):
        """将里程表设置为指定的数"""
        self.odometer_reading = mileage
        if mileage >= self.odometer_reading:
            self.odometer_reading = mileage
        else:
            print("You can't roll back an odometer!")

    def increment_odometer(self, miles):
        """让里程表读数增加指定的值"""
        self.odometer_reading += miles


class Battery:
    """一次模拟电动汽车电池的简单尝试"""
    def __init__(self, battery_size=40):
        self.battery_size = battery_size

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

    def get_range(self):
        """打印一条消息,指出电池的续航里程"""
        if self.battery_size == 40:
            range = 150
        elif self.battery_size ==65:
            range =225
        print(f"This car can go about {range} miles on a full charge")

    def upgrade_battery(self):
        if self.battery_size != 65:
            self.battery_size = 65


class ElectricCar(Car):
    """电动汽车的独到之处"""
    def __init__(self, make, model, year):
        """
        先初始化父类的属性,在初始化电动车特有的属性
        """
        super().__init__(make, model, year)
        self.battery = Battery()

主程序:

my_car.py

from car import ElectricCar

my_leaf = ElectricCar('nissan', 'leaf', 2024)
print(my_leaf.get_descriptive_name())
my_leaf.battery.describe_battery()
my_leaf.battery.get_range()

从一个模块中导入多个类:

可以根据需要再程序文件中导入任意数量的类。如果要在同一个程序中创建燃油汽车和电动汽车,就需要将Car类和ElectricCar类都引入

from car import ElectricCar,Car

my_leaf = ElectricCar('nissan', 'leaf', 2024)
print(my_leaf.get_descriptive_name())
my_leaf.battery.describe_battery()
my_leaf.battery.get_range()

my_mustang = Car('ford', 'mustang', 2024)
print(my_mustang.get_descriptive_name())

当从一个模块中导入多个类时,用逗号分隔各个类。导入必要的类后,就可根据需要创建每个类的任意数量的实例了

导入整个模块

还可以先导入整个模块,再使用点好访问需要的类。这种导入方法很简单,代码也易读,由于创建类实例的代码都包含模块名,因此不会与当前文件使用的任何名称发生冲突。

import car

my_leaf = car.ElectricCar('nissan', 'leaf', 2024)
print(my_leaf.get_descriptive_name())
my_leaf.battery.describe_battery()
my_leaf.battery.get_range()

my_mustang = car.Car('ford', 'mustang', 2024)
print(my_mustang.get_descriptive_name())

导入模块中的所有类:

要导入模块中的每个类,可使用下面的语法:

from module_name import *

不推荐这种导入方式:

一方面最好只看一下文件开头的import语句,就能清楚地知道程序使用了哪些类,但这种导入方式没有明确地指出使用了模块中的哪些类。

另一方面,这种导入方式还可能引发名称方面的迷惑,如果一不小心导入了一个与程序文件中的其他东西同名的类,将引发难易诊断的错误。

但是我们还是要去了解这种导入方式,因为我们可能在别人编写的代码中见到它

在一个模块中导入另一个模块

有时候,需要将类分散到多个模块中,以免模块太大或者在同一个模块中存储不相关的类,在将类存储在多个模块中时,你可能会发现一个模块中的类依赖于另一个模块中的类,在这种情况下,可在前一个模块中导入必要的类。

现在可以分别从每个模块中导入类,以根据需要创建任意类型的汽车了

from car import Car

from electric_car import ElectriCar

使用别名

给类指定别名

from electric_car import ElectricCar as EC

给模块指定别名

import electric_car as ec

找到合适的工作流程

一开始应让代码结构尽量简单,首先尝试在一个文件中完成所有的工作,确定一切都能正确运行后,在将类移到独立的模块中国,如果你喜欢模块和文件的交互方式,可在项目开始时就尝试将类存储到模块中。先找出让你能够编写出可行代码的方式,再尝试让代码更加整洁。

练习9.11

import statistic
user1 = statistic.Admin('xiong', 'haha', 18, 110)
user1.privilege.show_privileges()

练习9.12

statistic.py

class User:
    def __init__(self, first_name, last_name, age, phone_number):
        self.first_name = first_name
        self.last_name = last_name
        self.age = age
        self.phone_number = phone_number
        self.username = f"{self.first_name} {self.last_name}"
        self.log_attempts = 0

    def describe_user(self):
        print(f"user's name is {self.username}")
        print(f"{self.username} is {self.age} years old")
        print(f"{self.username}'s phone number is {self.phone_number}")

    def greet_user(self):
        print(f"Hello, {self.username}")

    def increment_login_attempts(self):
        self.log_attempts += 1

    def reset_login_attempts(self):
        self.log_attempts = 0

admin.py

import statistic


class Privileges:
    def __init__(self):
        self.privileges = ['can add post', 'can delete post', 'can ban user']

    def show_privileges(self):
        for privilege in self.privileges:
            print(f"manager's privileges include {privilege}")


class Admin(statistic.User):
    def __init__(self, first_name, last_name, age, phone_number):
        super().__init__(first_name, last_name, age, phone_number)
        self.privilege = Privileges()

main_idea.py

import admin
user1 = admin.Admin('xiong', 'haha', 18, 110)
user1.privilege.show_privileges()

Python标准库

python标准库是一组模块,在安装Python时以及包含在内。

我们可以使用标准库中的任何函数和类,只需在程序开头添加一条简单的import语句即可

例如模块random:

random中的一个有趣的函数是randint()。她将两个整数作为参数,并随机返回一个位于这两个整数之间(含)的整数。

from random import randint

print(randint(1, 6))

另一个很有用的参数是choice(),它将一个列表或元组作为参数,并随机返回其中一个元素

from random import choice

players = ['LBL', 'kari', 'KOBE', 'jordan']
first_up = choice(players)
print(first_up)

还可以从其他地方下载外部模块!

练习9.13

from random import randint


class Die:
    def __init__(self, sides):
        self.sides = sides

    def roll_die(self):
        print(randint(1, self.sides))


die1 = Die(6)
die1.roll_die()

die2 = Die(10)
die2.roll_die()

die3 = Die(20)
die3.roll_die()

练习9.14

from random import choice

statis = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'x', 'l', 'j', 'z', 'h']


def open_result(group):
    number = 0
    while number < 4:
        number += 1
        print(choice(group))


open_result(statis)

练习9.15

from random import choice

statis = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'x', 'l', 'j', 'z', 'h']

my_ticket = ['x', 'j', '3', '0']


def open_result(group):
    price_result = []
    number = 0
    while number < 4:
        number += 1
        price_result.append(choice(group))
    print(price_result)
    return price_result


number1 = 0
open_number = []
while open_number != my_ticket:
    number1 += 1
    open_number = open_result(statis)


print(f"循环了{number1}次才中奖了")

要了解Python标准库,一个很不错的资源是网站Python 3 Module of the Week

类的编程风格

类名应采用驼峰命名法,即将类名中的每个单词的首字母都大写,并且不适用下划线。

实例名和模块名都采用全小写格式,并在单词之间加上下划线。

对于每个类,都应该在类定义后面紧跟一个文档字符串,简要地描述类的功能。

每个模块后也都应包含一个文档字符串,对其中的类可用来做什么进行描述

在类中,可以使用一个空行来分隔方法

在模块中,可以使用两个空行来分隔类

当需要同时导入标准库中的模块和你编写的模块时,先编写导入标准模块的import语句,再添加一个空行,然后编写导入你自己编写的模块的import语句。这样让人更容易明白程序使用的模块来自哪里

  • 24
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值