Python练习:从入门到实践——类(class)

目录

一、创建和使用类

二、根据类创建实例

2.1 访问属性

2.2 调用方法

2.3 创建多个实例

三、使用类和实例

3.1 Car类

3.2  给属性指定默认值

3.3 修改属性的值

3.3.1 直接修改属性的值

3.3.2 通过方法修改属性的值

3.3.3 通过方法对属性的值进行递增

四、继承

4.1  子类的方法 __init__()

4.2 Python 2.7中的继承

4.3 给子类定义属性和方法

4.4 重写父类的方法

4.5 将实例用作属性

五、导入类

5.1 导入单个类

5.2 在一个模块中存储多个类

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

5.4 导入整个模块

5.5 导入模块中的所有类

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

六、Python标准库

七、类编码风格


编写类时,定义一大类对象都有的通用行为,基于类创建对象时,每个对象都自动具备这种通用行为,然后可根据需要赋予每个对象独特的个性。

根据类来创建对象被称为实例化。

一、创建和使用类

e.g 创建Dog类。根据Dog类创建的每个实例都将存储名字和年龄,赋予每条小狗蹲下(sit())和打滚(roll_over())的能力:

class Dog():
    """一次模拟小狗的简单尝试"""
    def __init__(self, name, age):
        """初始化属性name和age"""
        self.name = name
        self.age = age

    def sit(self):
        """模拟小狗命令时蹲下"""
        print(self.name.title() + " is now sitting.")

    def roll_over(self):
        """模拟小狗被命令时打滚"""
        print(self.name.title() + " rolled over!")

类的名称需要首字母大写。

1. 方法__init__()

类中的函数称为方法,有关函数的一切都适用于方法,唯一的差别在于调用方法的方式。

每次根据Dog类创建新实例时,Python都会自动运行方法__init__()。

形参self必不可少,且必须位于其他形参的前面;Python调用方法__init__()来创建Dog实例时,将自动传入实参self,每个与类相关联的方法调用都自动传递实参self,它是一个指向实例本身的引用,让实例能够访问类中的属性和方法。e.g 创建Dog实例时,Python将调用Dog类的方法__init__(),我们只需提供最后两个形参(name、age),self会自动传递。

2. self.name

以self为前缀的变量可供类中的所有方法使用,也可以通过类的任何实例来访问这些变量。self.name = name获取存储在形参name中的值,并将其存储到变量name中,然后该变量被关联到当前创建的实例。像这样,通过实例访问的变量称为属性。

二、根据类创建实例

class Dog():
    ---snip---

my_dog = Dog('while',6)
print("My dog's name is " + my_dog.name.title() +".")
print("My dog is " + str(my_dog.age) +" years old.")
  • 方法__init__()创建一个表示特定小狗的实例,并使用我们提供的值来设置属性name和age。
  • 方法__init__()并未显式地包含return语句,但python自动返回一个表示这条小狗的实例,并将该实例存储在变量my_dog中
  • 通常认为首字母大写的名称(e.g Dog)为类,而小写的名称(e.g my_dog)为根据类创建的实例。

2.1 访问属性

句点表示法。

my_dog.name
my_dog.age

2.2 调用方法

根据Dog类创建实例后,可以使用句点表示法来调用Dog类中定义的任何方法。

my_dog.sit()
my_dog.roll_over()

2.3 创建多个实例

可根据需求创建任意数量的实例,前提是将每个实例都存储在不同的变量中,或占用列表或字典的不同位置。

三、使用类和实例

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

3.1 Car类

class Car():
    def __init__(self, make, model, year):
        """初始化描述汽车的属性"""
        self.make = make
        self.model = model
        self.year = year

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

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

3.2  给属性指定默认值

类中的每个属性都必须有初始值,哪怕这个值是0或空字符串。在有些情况下,如设置默认值时,在方法__init__()内指定这种初始值时是可行的,如果对某个属性这样做了,就无需包含为它提供初始值的形参。

e.g 添加一个名为odometer_reading的属性,其初始值总是0,同时添加一个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 = 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.")

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

属性odometer_reading被赋予的初始值,没有包含在__init__()方法的形参中。

3.3 修改属性的值

3.3.1 直接修改属性的值

通过实例直接访问。

class Car():
    ---snip---

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

# 直接使用句点表示法访问并设置汽车的属性odometer_reading,并将其设置为23
my_new_car.odometer_reading = 23
my_new_car.read_odometer()

3.3.2 通过方法修改属性的值

无需直接访问属性,可将值传递给一个方法,由它在内部进行更新。

class Car():
    ---snip---

    def update_odometer(self,mileage):
        self.odometer_reading = mileage

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

my_new_car.update_olometer(23)
my_new_car.read_odometer()

对方法update_odometer()进行扩展,禁止将里程表读数往回调。

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

3.3.3 通过方法对属性的值进行递增

e.g 将属性值递增特定的量,而不是将其设置为全新的值。

假如我们购买了一辆二手车,且从购买到登记期间增加了100英里的里程。

class Car():
    ---snip---

    def update_odometer(self, mileage):
        ---snip---
    
    def increment_odometer(self,miles):
        """将里程表读数增加指定的量"""
        if miles >= 0:
            self.odometer_reading += miles
        else:
            print("You can't increase the minus odometer!"


my_used_car = Car('subaru','outback',2013)
print(my_used_car.get_descriptive_name())

my_used_car.update_odometer(23500)
my_used_car.read_odometer()

my_used_car.increment_odometer(100)
my_used_car.read_odometer()

四、继承

  • 如果要编写的类是另一个现成类的特殊版本,可以使用继承;
  • 一个类继承另一个类,它将自动获得另一个类的所有属性和方法;
  • 原有的类称为父类,新类称为子类,子类继承了父类的所有属性和方法,同时还可以定义自己的属性和方法

4.1  子类的方法 __init__()

e.g 模拟电动汽车。创建一个简单的ElecticCar类版本,具备Car类的所有功能。

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 >= 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 __init__(self, make, model, year):
        """ 初始化父类的属性"""
        super().__init__(make,model,year)

my_tesla = ElectricCar('tesla','model s',2016)
print(my_tesla.get_descriptive_name())
  1. 创建子类时,父类必须包含在当前文件中,且位于子类的前面;
  2. 定义子类时,必须在括号内指定父类的名称 class ElectricCar(Car);
  3. super()是一个特殊的函数,帮助Python将父类和子类关联起来,super().__init__(make, model, year)调用父类的方法__init__(),让ElectricCar实例包含父类的所有属性,父类也称为超类(super因此得名)。

4.2 Python 2.7中的继承

class Car(object):
    def __init__(self, make, model, year):
        ---snip---

class ElectricCar(Car):
    def __init__(self, make, model, year):
        super(ElectricCar,self).__init__(make, model, year)
        ---snip---
  • super()需要两个实参:子类名和对象self
  • 在Python2.7中使用继承时,务必在定义父类时在括号内指定object

4.3 给子类定义属性和方法

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

e.g 添加电动汽车的特有属性(电瓶),以及一个描述该属性的方法;存储电瓶容量并编写一个打印电瓶描述的方法。

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.")

my_tesla = ElectricCar('tesla','model s',2016)
print(my_tesla.get_descriptive_name())
my_tesla.describe_battery()   

4.4 重写父类的方法

  • 对于父类的方法,只要它不符合子类模拟的实例的行为,都可对其进行重写;
  • 重写,即子类中的方法与父类中的方法同名,这样Python不会考虑父类中的这个方法,而只关注子类中定义的方法。

4.5 将实例用作属性

可以将类的一部分作为一个独立的类提取出来,即将一个大型类拆分成多个协同工作的小类。

e.g 将针对汽车电瓶的属性和方法,放到另一个类Battery()中,并将Battery实例用作ElectricCar类的一个属性。

class Car():
    ---snip---

class Battery():
    """模拟电动汽车的电瓶"""
    def __init__(self, battery_size=70):
        self.battery_size = battery

    def describe_battery():
        print("This car has a " + str(battery_size) +"-kWh battery.")

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

my_tesla = ElectricCar('Tesla','model s',2016)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()
        

e.g 再给Battery类添加一个方法,根据其电瓶容量报告汽车的续航里程。

class Car():
    --snip--

class Battery():
    --snip--
    
    def get_range(self):
        """打印一条消息,指出电瓶的续航里程"""
        if self.battery_size == 70:
            range = 240
        elif self.battery_size ==85:
            range = 270

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

class ElectricCar(Car):
    --snip--

my_tesla = ElectricCar('Tesla','model s',2016)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()
my_tesla.battery.get_range()

五、导入类

5.1 导入单个类

可以将类Car 放在一个单独的文件car.py中,然后使用语句:

from car import Car

导入类Car。

5.2 在一个模块中存储多个类

import 后加不同的类名,导入不同的类。

from car import Car
from car import ElectricCar
from car import Battery
from car import ElectricCar

my_tesla = ElectricCar('tesla','model s',2016)

print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()
my_tesla.battery.get_range()

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

可根据需要在程序文件中导入任意数量的类,使用逗号,分隔各个类。

from car import Car,ElectricCar

5.4 导入整个模块

使用句点表示法访问需要的类。

import car

my_car = car.Car('audi','a4',2016)

5.5 导入模块中的所有类

要导入模块中的每个类,使用*

from module_name import *
  • 不建议使用这种方法,容易引发名称错误
  • 需要从一个模块导入很多类时,最好导入整个模块,然后使用句点法来访问类module_name.class_name

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

  • 有时,需要将类分散到多个模块中,以免模块太大,或在同一个模块中存储不相关的类;
  • 将类存储在多个模块中时,一个模块中的类kennel依赖于另一个模块中的类,在这种情况下就有必要在前一个模块中导入必要的类

e.g 将Car类存储在car.py模块中,将ElectricCar和Battery类存储在另一个模块electric_car.py中。

from car import Car

class Battery():
    --snip--


class ElectricCar(Car):
    --snip--

六、Python标准库

  • 模块collection中的一个类OrderedDict,创建的字典与普通字典的区别是可以记录键-值对的添加顺序
  • 模块random包含以各种方式生成随机数的函数,其中randint()返回位于指定范围内的整数。

e.g 创建一个Die类,它包含一个名为sides的属性,该属性的默认值为6,编写一个名为roll_die()的方法,它位于1和骰子面数之间的一个随机数,创建一个6面的骰子,掷10次。创建一个10面和一个20面的骰子,并将他们都掷10次。

from random import randint

class Die():

    def __init__(self, sides=6):
        self.sides = sides

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


my_die_6 = Die()
print("\n Roll Die-6:")
for i in range(0,10):
    my_die_6.roll_die()

my_die_10 = Die(10)
print("\nRoll Die-10:")
for i in range(0,10):
    my_die_10.roll_die()

my_die_20 =Die(20)
print('\nRoll Die-20:')
for i in range(0,10):
    my_die_20.roll_die()

七、类编码风格

  • 类名采用驼峰命名法,每个单词的首字母大写,单词之间不使用下划线;实例名和模块名采用小写格式,单词之间使用下划线;
  • 类定义后包含文档字符串简要描述类的功能;
  • 在类中,使用一个空行分隔方法;在模块中,使用两个空行分隔类;
  • 需要同时导入标准库中的模块和自己编写的模块时,先编写导入标准库模块的import语句,添加一个空行,再编写导入自己编写的模块的import语句。

 

你可能感兴趣的:(编程练习)