当前位置 : 主页 > 编程语言 > python >

教师妹学python之七:面向对象编程

来源:互联网 收集:自由互联 发布时间:2022-06-18
目录 Python的面向对象编程是什么? 类的定义 类与实例 定义一个类 实例化对象 类和实例属性 实例方法 测验 类继承 狗公园的例子 父类与子类 扩展父类的功能 测验 结论 ​面向对象编


教师妹学python之七:面向对象编程_实例化

目录

  • Python的面向对象编程是什么?
  • 类的定义
  • 类与实例
  • 定义一个类
  • 实例化对象
  • 类和实例属性
  • 实例方法
  • 测验
  • 类继承
  • 狗公园的例子
  • 父类与子类
  • 扩展父类的功能
  • 测验
  • 结论

​面向对象编程​(OOP)是一种通过将相关属性和行为绑定到单个​对象中​来构造程序的方法。从概念上讲,对象就像系统的组成部分,可以将程序想像成工厂的流水线,在流水线的每个步骤中,系统组件都会处理一些材料,最终将原材料换为成品。

​一个对象包含数据(例如流水线上每个步骤的原始材料)、行为(例如每个流水线组件可以执行的动作)。​

本教程将介绍:

  • 创建一个​类​
  • 使用类​创建新对象​
  • 具有​类继承的​模型系统

Python的面向对象编程是什么?

面向对象的编程是一种​编程范例​,它提供了一种结构化程序的方法,以便将属性和行为捆绑到单个对象中。

例如,对象可能代表一个人​的属性​(如姓名,年龄和地址)和​行为​(如走路,说话,呼吸和运行)。

换句话说,面向对象的编程是一种对具体的,现实世界中的事物(例如汽车)以及事物之间的关系(例如公司与员工,学生和教师等等)进行建模的方法。OOP将现实世界的实体建模为软件对象,这些对象具有与之关联的一些数据并且可以执行某些功能。

另一个常见的编程范例是面向​过程编程​,它像配方一样构造程序,因为它以功能和代码块的形式提供了一组步骤,这些步骤按顺序完成任务。

类的定义

​基础数据结构​(例如数字、字符串和列表)旨在表示简单的信息,例如苹果的价格、一首诗的名称或你喜欢的颜色。如果你想代表更复杂的东西怎么办?

例如,假设你要掌握员工动态。你需要存储每个员工的一些基本信息,例如姓名、年龄、职位以及开始工作年份。

一种方法是将每个员工表示为一个列表:

kirk = ["James Kirk", 34, "Captain", 2265]
spock = ["Spock", 35, "Science Officer", 2254]
mccoy = ["Leonard McCoy", "Chief Medical Officer", 2266]

但这种方法存在诸多问题。

首先,它会使大型代码文件更难管理。如果​​kirk[0]​​​在​​kirk​​​声明列表的位置之外引用几行,你是否还记得带有index的元素​​0​​是员工的姓名?

其次,如果不是每个员工在列表中都有相同数量的元素,则可能会引入错误。在​​mccoy​​​的列表中缺少年龄信息,因此​​mccoy[1]​​​将返回​​"Chief Medical Officer"​​而不是McCoy的年龄。

​使此类代码更易于管理和维护的一种好方法是使用类。​

类与实例

类用于创建用户定义的数据结构。类可以定义函数,函数可以利用实例中的数据执行一定的行为。

创建一个​​Dog​​类,该类存储有关单个狗的特征和行为信息。​实例​是从类构建的并包含实际数据的对象,​​Dog​​类的实例是一条真实的狗,名字像迈尔斯(Miles),已经四岁了。

换句话说,课程就像表格一样,实例就像已经填写了信息的表格,就像许多人可以用自己的独特信息填写相同的表格一样,可以从一个类中创建许多实例。

定义一个类

所有类定义均以​​class​​关键字开头,后跟类名和冒号。在类定义下缩进的任何代码均被视为类主体的一部分。

这是一个​​Dog​​类的示例:

class Dog:
pass

​​Dog​​​类的主体由一个语句组成:​​pass​​​关键字。​​pass​​通常用作占位符,指示代码最终将到达何处。它允许你在不引发Python错误的情况下运行此代码。

​注意:​ Python类名称是按照惯例用大写字母表示法编写的。

所有​​Dog​​​对象必须具有的属性在​​.__init__()​​​方法中定义。每次创建新​​Dog​​​对象时,​​.__init__()​​通过分配对象属性的值来设置对象的初始​状态​。即​​.__init__()​​可以初始化该类的每个新实例。

让我们使用​​.__init__()​​​为Dog类增加​​.name​​​和​​.age​​属性:

class Dog:
def __init__(self, name, age):
self.name = name
self.age = age

请注意,​​.__init__()​​​方法缩进了四个空格。该方法的主体缩进了八个空格,这种缩进至关重要,它告诉Python ​​.__init__()​​​方法属于​​Dog​​该类。

在​​.__init__()​​​的主体中,有两个使用​​self​​变量的语句:

  • ​​self.name = name​​​创建一个名为name的属性,并为其分配​​name​​参数的值。
  • ​​self.age = age​​​创建一个名为age的属性,​​age​​​并为其分配​​age​​参数的值。
  • 在​​.__init__()​​中创建的属性称为​实例属性​。实例属性的值特定于类的特定实例。所有​​Dog​​​对象都有名称和年龄,但是​​name​​​和​​age​​​属性的值将根据​​Dog​​实例而有所不同。

    另一方面,​类属性​是对于所有类实例具有相同值的属性。当然你也可以在​​.__init__()​​之外定义类的属性。

    例如,以下​​Dog​​​类具有一个名为​​species​​​的类属性,值为​​"Canis familiaris"​​:

    class Dog:
    # Class attribute
    species = "Canis familiaris"
    def __init__(self, name, age):
    self.name = name
    self.age = age

    类属性直接在类名称的第一行下方定义,并以四个空格缩进,必须对其初始化。创建类的实例时,将自动创建类属性并将其分配给它们的初始值。

    实例化对象

    打开IDLE的交互式窗口,然后键入以下内容:

    class Dog:
    pass

    ​​Dog​​类没有属性或方法的新类。

    创建类的对象称为​实例化​对象。你可以通过键入​​Dog​​类的名称来实例化一个新对象:

    >>> Dog()
    <__main__.Dog object at 0x106702d30>

    现在,有了一个新​​Dog​​​对象​​0x106702d30​​。这个看起来很有趣的字母和数字字符串是一个​内存地址​,用于指示​​Dog​​对象在计算机内存中的存储位置。

    现在实例化第二个​​Dog​​对象:

    >>> Dog()
    <__main__.Dog object at 0x0004ccc90>

    新​​Dog​​​实例位于其他内存地址,那是因为它是一个全新的实例,并且与实例化的第一个​​Dog​​对象完全不同,要以另一种方式查看此信息,请输入以下内容:

    >>> a = Dog()
    >>> b = Dog()
    >>> a == b
    False

    在此代码中,将创建两个新​​Dog​​​对象,并将它们分配给变量​​a​​​和​​b​​​。比较​​a​​​、​​b​​​使用​​==​​​运算符时,结果为​​False​​​。即使​​a​​​和​​b​​​都是​​Dog​​类的实例,它们也代表内存中的两个不同的对象。

    类和实例属性

    现在创建一个新​​Dog​​​类,其类属性为​​.species​​​,两个实例属性为​​.name​​​和​​.age​​:

    >>> class Dog:
    ... species = "Canis familiaris"
    ... def __init__(self, name, age):
    ... self.name = name
    ... self.age = age

    对Dog类实例化一个对象,你需要提供​​name​​​和​​age​​​属性值。如果不这样做,Python会引发一个​​TypeError​​:

    >>> Dog()
    Traceback (most recent call last):
    File "<pyshell#6>", line 1, in <module>
    Dog()
    TypeError: __init__() missing 2 required positional arguments: 'name' and 'age'

    要将参数值传递给​​name​​​和​​age​​参数:

    >>> buddy = Dog("Buddy", 9)
    >>> miles = Dog("Miles", 4)

    这将创建两个新​​Dog​​实例-一个实例用于名为Buddy的9岁狗,另一个实例是名为Miles的4岁狗。

    ​​Dog​​​类的​​.__init__()​​方法有三个参数,那么为什么只传递给它的两个参数呢?

    实例化​​Dog​​​对象时,Python会创建一个新实例并将其传递给的第一个参数​​.__init__()​​​。这实际上删除了​​self​​​参数,因此只需要​​name​​​and​​age​​参数即可。

    创建​​Dog​​实例后,可以使用​‘.’​访问它们的实例属性:

    >>> buddy.name
    'Buddy'
    >>> buddy.age
    9
    >>> miles.name
    'Miles'
    >>> miles.age
    4

    可以通过以下方式访问类属性:

    >>> buddy.species
    'Canis familiaris'

    使用类组织数据的最大优势之一是可以确保实例具有你期望的属性。所有​​Dog​​​实例都具有​​.species​​​,​​.name​​​和​​.age​​属性,因此可以放心使用这些属性,因为它们将始终返回值。

    尽管保证属性存在,但是也可以​动态​更改它们的值:

    >>> buddy.age = 10
    >>> buddy.age
    10
    >>> miles.species = "Felis silvestris"
    >>> miles.species
    'Felis silvestris'

    在此示例中,​​buddy​​​对象的​​.age​​​属性值更改为​​10​​​,将​​miles​​​对象的species属性值更改为​​"Felis silvestris"​​。

    默认情况下自定义对象是可变的。如果可以动态更改对象,则该对象是可变的。例如,列表和字典是可变的,但字符串和元组是不可变的。

    实例方法

    ​实例方法​是在类内部定义的函数,只能从该类的实例中调用。就像​​.__init__()​​​实例方法的第一个参数始终是​​self​​。

    在IDLE中打开一个新的编辑器窗口,然后输入​​Dog​​类:

    class Dog:
    species = "Canis familiaris"
    def __init__(self, name, age):
    self.name = name
    self.age = age
    # Instance method
    def description(self):
    return f"{self.name} is {self.age} years old"
    # Another instance method
    def speak(self, sound):
    return f"{self.name} says {sound}"

    ​​Dog​​类具有两个实例方法:

  • ​​.description()​​ 返回显示狗的名字和年龄。
  • ​​.speak()​​有一个sound参数,并返回一个字符串,中包含狗的名字和狗发出的声音。
  • 将修改后的​​Dog​​​类保存到一个名为​​dog.py​​的文件中,然后F5运行该程序,然后输入以下内容以查看实例方法的运行情况:

    >>> miles = Dog("Miles", 4)
    >>> miles.description()
    'Miles is 4 years old'
    >>> miles.speak("Woof Woof")
    'Miles says Woof Woof'
    >>> miles.speak("Bow Wow")
    'Miles says Bow Wow'

    在上述​​Dog​​​类中,​​.description()​​​返回一个字符串,其中包含有关​​Dog​​​类实例对象的信息​​miles​​。

    创建​​list​​​对象时,可以​​print()​​用来显示列表的字符串:

    >>> names = ["Fletcher", "David", "Dan"]
    >>> print(names)
    ['Fletcher', 'David', 'Dan']

    让我们看看​​print()​​​使用​​miles​​对象时会发生什么:

    >>> print(miles)
    <__main__.Dog object at 0x00aeff70>

    当​​print(miles)​​​收到一条看起来很神秘的消息时,会告诉你这​​miles​​​是​​Dog​​​内存地址中的一个对象​​0x00aeff70​​​。你可以通过定义特殊的实例方法来更新打印的内容​​.__str__()​​。

    在编辑器窗口中,将​​Dog​​​类的​​.description()​​​方法名称更新为​​.__str__()​​:

    class Dog:
    # Leave other parts of Dog class as-is
    # Replace .description() with __str__()
    def __str__(self):
    return f"{self.name} is {self.age} years old"

    保存文件,然后按F5。现在使用时​​print(miles)​​,你将获得输出:

    >>> miles = Dog("Miles", 4)
    >>> print(miles)
    'Miles is 4 years old'

    ​​.__init__()​​​和​​.__str__()​​ 称之为​构造函数,​因为它们以双下划线开头和结尾。你可以使用许多​构造函数​来自定义Python中的类。尽管对于一本入门级的Python教程来说,这个话题太高级了,但是了解​构造函数​是掌握Python中的面向对象程序设计的重要组成部分。

    小测验

    练习:创建汽车课程显示隐藏

    创建Car类,具有两个属性:
    .color,它以字符串形式存储汽车颜色的名称
    .mileage,它以整数形式存储汽车行驶的英里数
    然后实例化两个Car对象-行驶20,000英里的蓝色汽车和行驶30,000英里的红色汽车-并打印出它们的颜色和行驶里程。输出应如下所示:
    The blue car has 20,000 miles.
    The red car has 30,000 miles.

    解决方案:

    首先,创建一个​​Car​​​具有​​.color​​​和​​.mileage​​实例属性的类:

    class Car:
    def __init__(self, color, mileage):
    self.color = color
    self.mileage = mileage

    ​​color​​​和​​mileage​​​参数分配给​​.__init__()​​​函数参数​​self.color​​​和​​self.mileage​​。

    现在,你可以创建两个​​Car​​实例:

    blue_car = Car(color="blue", mileage=20_000)
    red_car = Car(color="red", mileage=30_000)

    该​​blue_car​​​实例由值传递创建​​"blue"​​​的​​color​​​参数,并​​20_000​​​为​​mileage​​​参数。同样,​​red_car​​​使用​​"red"​​​和创建值​​30_000​​。

    要打印每个​​Car​​​对象的颜色和里程,可以循环显示​​tuple​​包含两个对象的:

    for car in (blue_car, red_car):
    print(f"The {car.color} car has {car.mileage:,} miles")

    类继承

    ​​继承​​​是一个类​​继承​​另一个类的属性和方法的过程,新形成的类称为​子类​,子类派生自的​类​称为​父类​。

    子类可以扩展父类的属性和方法。换句话说,子类继承了父级的所有属性和方法,但也可以指定自己唯一的属性和方法。尽管这样的类推并不恰当,但是你可以想到对象继承有点像遗传继承。

    你可能是从母亲那里继承了头发的颜色,这是你与生俱来的属性。假设你决定将头发染成紫色。假设你的母亲没有紫色头发,那么你刚刚​覆盖​了从母亲那里继承的头发颜色属性。

    从某种意义上说,你还从你的父母那里继承了语言。如果你的父母说英语,那么你也会说英语。现在,假设你决定学习第二种语言,例如德语。在这种情况下,你​扩展​了语言属性,因为你具备了父母没有的属性。

    狗公园的例子

    假装你在狗公园里。公园里有许多不同品种的狗,它们都有各种各样的狗行为。

    现在需要使用Python类对狗公园进行建模。如果使用上一节中编写的Dog类(按名称和年龄来区分狗),则不能满足按品种来区分狗。

    当然你可以为Dog类添加​​.breed​​属性:

    class Dog:
    species = "Canis familiaris"
    def __init__(self, name, age, breed):
    self.name = name
    self.age = age
    self.breed = breed

    现在,你可以通过在交互式窗口中实例化一堆不同的狗来对狗公园进行建模:

    >>> miles = Dog("Miles", 4, "Jack Russell Terrier")
    >>> buddy = Dog("Buddy", 9, "Dachshund")
    >>> jack = Dog("Jack", 3, "Bulldog")
    >>> jim = Dog("Jim", 5, "Bulldog")

    每个品种的狗都有略有不同的行为。例如,斗牛犬叫声听起来很像​woof​,而腊肠犬的叫声更高,听起来更像是​yap​。

    仅使用​​Dog​​​类,每次在实例上调用它时,都必须为Dog类​​.speak()​​提供一个字符串:

    >>> buddy.speak("Yap")
    'Buddy says Yap'
    >>> jim.speak("Woof")
    'Jim says Woof'
    >>> jack.speak("Woof")
    'Jack says Woof'

    父类与子类

    让我们为上述三个品种中的每个品种创建一个子类:Jack Russell Terrier,Dachshund和Bulldog。以下是​​Dog​​类的完整定义:

    class Dog:
    species = "Canis familiaris"
    def __init__(self, name, age):
    self.name = name
    self.age = age
    def __str__(self):
    return f"{self.name} is {self.age} years old"
    def speak(self, sound):
    return f"{self.name} says {sound}"

    请记住,要创建子类,请使用其自己的名称创建新类,然后将父类的名称放在括号中。将以下内容添加到​​dog.py​​​文件中,以创建​​Dog​​类的三个新的子类:

    class JackRussellTerrier(Dog):
    pass
    class Dachshund(Dog):
    pass
    class Bulldog(Dog):
    pass

    按F5以保存并运行文件。定义了子类后,现在可以在交互式窗口中实例化某些特定品种的狗:

    >>> miles = JackRussellTerrier("Miles", 4)
    >>> buddy = Dachshund("Buddy", 9)
    >>> jack = Bulldog("Jack", 3)
    >>> jim = Bulldog("Jim", 5)

    子类的实例继承了父类的所有属性和方法:

    >>> miles.species
    'Canis familiaris'
    >>> buddy.name
    'Buddy'
    >>> print(jack)
    Jack is 3 years old
    >>> jim.speak("Woof")
    'Jim says Woof'

    要确定给定对象属于哪个类,可以使用内置的​​type()​​:

    >>> type(miles)
    <class '__main__.JackRussellTerrier'>

    如果要确定是否​​miles​​​也是​​Dog​​​该类的实例怎么办?可以使用内置的方法执行此操作​​isinstance()​​:

    >>> isinstance(miles, Dog)
    True

    注意,​​isinstance()​​​带有两个参数,一个对象和一个类。在上面的示例中,​​isinstance()​​​检查​​miles​​​是否是​​Dog​​​类的实例,然后返回​​True​​。

    ​​miles​​​,​​buddy​​​,​​jack​​​和​​jim​​​对象都是​​Dog​​​实例,但​​miles​​​不是​​Bulldog​​​的实例,而​​jack​​​不是​​Dachshund​​的实例:

    >>> isinstance(miles, Bulldog)
    False
    >>> isinstance(jack, Dachshund)
    False

    扩展父类的功能

    由于不同品种的狗的吠声略有不同,因此需要为其各自​​.speak()​​​方法的参数提供默认值。为此,你需要​​.speak()​​在每个类定义中覆盖。

    要覆盖父类定义的方法,请在子类上定义一个具有相同名称的方法。

    class JackRussellTerrier(Dog):
    def speak(self, sound="Arf"):
    return f"{self.name} says {sound}"

    现在​​.speak()​​​在​​JackRussellTerrier​​​类中定义了默认参数,​​sound​​​将其设置为​​"Arf"​​。

    ​​dog.py​​​使用新的​​JackRussellTerrier​​类进行更新,然后按F5保存并运行文件。

    >>> miles = JackRussellTerrier("Miles", 4)
    >>> miles.speak()
    'Miles says Arf'

    有时狗会发出不同的吠叫,因此,如果Miles生气咆哮时,仍然可以用​​.speak()​​不同的声音:

    >>> miles.speak("Grrr")
    'Miles says Grrr'

    关于类继承要记住的是,对父类的更改会自动传播到子类。

    例如,改变​​Dog​​​类​​.speak()​​的返回值:

    class Dog:
    # Leave other attributes and methods as they are
    # Change the string returned by .speak()
    def speak(self, sound):
    return f"{self.name} barks: {sound}"

    保存文件,然后按F5。现在,当你创建一个​​Bulldog​​​名为的新实例时​​jim​​​,​​jim.speak()​​将返回新字符串:

    >>> jim = Bulldog("Jim", 5)
    >>> jim.speak("Woof")
    'Jim barks: Woof'

    但是,调用​​.speak()​​​一个​​JackRussellTerrier​​实例不会显示输出的新风格:

    >>> miles = JackRussellTerrier("Miles", 4)
    >>> miles.speak()
    'Miles says Arf'

    可以使用​​super()​​方法从子类的方法内部访问父类:

    class JackRussellTerrier(Dog):
    def speak(self, sound="Arf"):
    return super().speak(sound)

    当你调用​​super().speak(sound)​​​,Python搜索父类​​Dog​​​的​​.speak()​​方法。

    >>> miles = JackRussellTerrier("Miles", 4)
    >>> miles.speak()
    'Miles barks: Arf'

    现在,当你调用时​​miles.speak()​​​,你将看到​​Dog​​类中新格式的输出。

    ​注意:​在以上示例中,​类层次结构​非常简单。​​JackRussellTerrier​​​类有一个父类​​Dog​​。在实际示例中,类层次结构可能会变得非常复杂。

    ​​super()​​不仅可以在父类中搜索方法或属性,还可以做更多的事情。它遍历整个类层次结构以找到匹配的方法或属性。

    小测验

    练习:

    创建一个​​GoldenRetriever​​​从​​Dog​​​该类继承的类。给出默认值为的​​sound​​​参数。对父类使用以下代码:​​GoldenRetriever.speak()​​​​"Bark"​​​​Dog​​

    class Dog:
    species = "Canis familiaris"
    def __init__(self, name, age):
    self.name = name
    self.age = age
    def __str__(self):
    return f"{self.name} is {self.age} years old"
    def speak(self, sound):
    return f"{self.name} says {sound}"

    解决方案:

    创建一个名为​​GoldenRetriever​​​的​​Dog​​​类,并覆盖Dog类的​​.speak()​​方法:

    class GoldenRetriever(Dog):
    def speak(self, sound="Bark"):
    return super().speak(sound)

    ​​GoldenRetriever.speak()​​​的sound参数默认值为​​"Bark"​​​,然后使用​​super()​​​用来调用父类的​​.speak()​​​方法,为​​GoldenRetriever​​​类的​​.speak()​​​方法传递相同的​​sound​​参数。

    总结

    ​本教程学习内容:​

    • 定义一个​class​
    • 实例化类中的​对象​
    • 使用​属性​和​方法​定义对象的​属性​和​行为​
    • 使用​继承​从​父类​创建​子类​
    • 使用以下方法引用父类上的方法​​super()​​
    • 使用以下命令检查对象是否从另一个类继承​​isinstance()​​



    上一篇:教师妹学python之五:数据结构
    下一篇:没有了
    网友评论