WHCSRL 技术网

Python学习笔记(八)——类

在学习类前,我们需要知道什么时面向对象编程:
1、面向对象编程(oop)是一种程序设计思想。oop把对象作为程序的基本单元,一个对象包含数据和操作数据的函数
2、在python中,所有数据类型都被视为对象,也可以自定义对象。自定义对象数据类型就是面向对象中类的概念
需要学习的部分
1、类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例
2、方法:类中定义的函数
3、类变量(属性):类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体(方法)之外。类变量通常不作为实例变量使用,类变量也称作属性
4、数据成员:类变量或者实例变量用于处理类及其实例对象的相关的数据
5、方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写
6、实例变量:定义在__init__方法中的变量,只作用于当前实例的类
7、继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待,以普通的类为基础建立专门的类对象
8、实例化:创建一个类的实例,类的具体对象。一个类可以实例化出无数个对象
9、对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法
10、多态:对不同类的对象使用同样的操作
11、封装:对外部世界隐藏对象的工作细节

一 创建和使用类

使用类几乎可以模拟任何东西

1.创建Dog类

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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

方法__init__():
类中的函数被称为方法,我们价格方法__init__()定义成了三个形参:self,name,age。
Python 调用这个 init() 方法来创建 Dog 实例时,将自动传入实参 self 。每个与类相关联的方法调用都自动传递实参 self ,它是一个指向实例本身
的引用,让实例能够访问类中的属性和方法。

2.根据类创建实例

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

my_dog = Dog('whillie', 6)
print("my dog's name is " + my_dog.name.title() + ".")
print("my dog's age is " + str(my_dog.age) + " years old.")

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

1.访问属性

要访问实例的属性,可以使用到句点表示法。

my_dog.name
  • 1

2.调用方法

class Dog():
--snip--
my_dog = Dog('willie', 6)
my_dog.sit()
my_dog.roll_over()
  • 1
  • 2
  • 3
  • 4
  • 5

要调用方法,可以指定实例的名称和要调用的方法,并用句点分隔它们。

3.创建多个实例

可以根据需求创建任意数量的实例。它们之间相互独立。

class Dog():
--snip--
my_dog = Dog('willie', 6)
your_dog = Dog('lucy', 3)
print("My dog's name is " + my_dog.name.title() + ".")
print("My dog is " + str(my_dog.age) + " years old.")
my_dog.sit()
print("
Your dog's name is " + your_dog.name.title() + ".")
print("Your dog is " + str(your_dog.age) + " years old.")
your_dog.sit()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

二 使用类和实例

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())
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2.给属性指定默认值

类的每个属性都要有初始值,哪怕这个值是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 = 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()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

3.修改属性的值

a.直接修改属性的值

要修改属性的值,最简单的就是直接修改

class Car():
--snip--

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

my_new_car.odometer_reading = 23
my_new_car.read_odometer()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

有时候需要这样直接访问属性,但有事需要编写对属性更新的方法

b.通过方法修改属性的值

如以上的例子,可以在类里多编写一个方法,应该放在def read_odometer前面

    def update_odometer(self,mileage):
        self.odometer_reading = mileage
  • 1
  • 2
c.通过方法对属性的值进行递增

有时候需要给属性增值特定的量,而不是设置为全新的值。本质还是通过方法改变属性值

def increment_odometer(self, miles):
    """ 将里程表读数增加指定的量 """
    self.odometer_reading += miles
  • 1
  • 2
  • 3

三 继承

编写类时,并非总是要从空白开始。如果我们编写的类时另一个现成类的特殊版本,可使用继承。

1.子类的方法__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 = 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
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

首先时Car类的代码,创建子类时,父类必须包含在当前文件中,且位于子类前面。定义子类时,也必须要在括号内指定父类名称。此时super()是一个特殊函数,帮助父类与子类关联起来

2.给子类定义属性和方法

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

class ElectricCar(Car):
    """Represent aspects of a car, specific to electric vehicles."""
    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()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

在这里我们添加了新属性及其初始值。

3.重写父类的方法

对于父类的方法,只要他不符合子类模拟的实物的行为,可以对其重写。

4.将实例用作属性

在使用代码模拟实物时,我们可能会发现给类添加的细节越来越多。此时我们可以把大型类拆分许多个协同工作的小类。

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.")
        
class ElectricCar(Car):
    """ 电动汽车的独特之处 """
    def __init__(self, make, model, year):
        """
        初始化父类的属性,再初始化电动汽车特有的属性
        """
        super().__init__(make, model, year)
        self.battery = Battery()

my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

我们添加了一个名为self.battery的属性。这行代码让Python创建了一个新的实例。

四 导入类

1.导入单个类

我们先编写一个只包含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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

我们利用import语句让Python打开模块car

from car import Car

my_new_car = Car('audi', 'a4', 2016)
print(my_new_car.get_descriptive_name())
my_new_car.odometer_reading = 23
my_new_car.read_odometer()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

2.在一个模块中存储多个类

一个人模块中可以存储多个类,只要在import标注好类名即可调用

3.从一个模块中导入多个类

可根据需要在程序文件导入任意数量的类。只需要将类名都写出来即可。

4.导入整个模块

我们也可以导入整个模块,然后使用句点表示法访问需要的类。

5.导入模块中的所有类

这样可以导入模块中所有的类

from module_name import *
  • 1

并不推荐这种导入方式,因为:
首先,如果只要看一下文件开头的 import 语句,就能清楚地知道程序使用了哪些类,将大有裨益;但这种导入方式没有明确地指出你
使用了模块中的哪些类。这种导入方式还可能引发名称方面的困惑。如果你不小心导入了一个与程序文件中其他东西同名的类,将引发难以诊断的错误。这里之所以介绍这种导
入方式,是因为虽然不推荐使用这种方式,但你可能会在别人编写的代码中见到它。

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

有时我们需要把子类存储在不同的模块里,就需要使用到从模块导入到另一个模块:

from car import Car
class Battery():
    --snip--
class ElectricCar(Car):
    --snip--
  • 1
  • 2
  • 3
  • 4
  • 5

五 Python标准库

Python标准库是一组模块。

六 类编码风格

类名应采用驼峰命名法,即将类名中的每个单词的首字母都大写,而不使用下划线。实例名和模块名都采用小写格式,并在单词之间加上下划线。

推荐阅读