ZetCode

Python 中的面向对象编程

最后修改于 2023 年 10 月 27 日

在本节 Python 教程中,我们将讨论 Python 中的面向对象编程。

那里有三种广泛使用的编程范式:过程式编程、函数式编程和面向对象编程。Python 支持所有这三种编程范式。

面向对象编程

面向对象编程 (OOP) 是一种使用对象及其交互来设计应用程序和计算机程序的编程范例。

OOP 中有一些基本的编程概念

抽象是通过为问题建模适当的类来简化复杂现实。多态是通过对不同的数据输入以不同的方式使用运算符或函数的过程。封装将类的实现细节隐藏在其他对象之外。继承是利用已定义的类来形成新类的一种方式。

Python 对象

Python 中的一切都是对象。对象是 Python OOP 程序的基本构建块。

object_types.py
#!/usr/bin/env python

# object_types.py

import sys

def function():
    pass

print(type(1))
print(type(""))
print(type([]))
print(type({}))
print(type(()))
print(type(object))
print(type(function))
print(type(sys))

在这个例子中,我们展示了所有这些实体实际上都是对象。type 函数返回指定对象的类型。

$ ./object_types.py 
<class 'int'>
<class 'str'>
<class 'list'>
<class 'dict'>
<class 'tuple'>
<class 'type'>
<class 'function'>
<class 'module'>

整数、字符串、列表、字典、元组、函数和模块都是 Python 对象。

Python class 关键字

之前的对象都是 Python 编程语言的内置对象。用户定义的对象是使用 class 关键字创建的。类是定义未来对象性质的蓝图。我们从类构建实例。实例是从特定类创建的特定对象。例如,Huck 可能是 Dog 类的实例。

first_object.py
#!/usr/bin/env python

# first_object.py

class First:
    pass

fr = First()

print(type(fr))
print(type(First))

这是我们的第一个类。现在类的正文是空的。给类名首字母大写是一个约定。

class First:
    pass

这里我们定义了 First 类。请注意,默认情况下,所有类都继承自基类 object

fr = First()

这里我们创建了 First 类的一个新实例。或者换句话说,我们实例化了 First 类。fr 是指向我们新对象的引用。

$ ./first_object.py 
<class '__main__.First'>
<class 'type'>

这里我们看到 frFirst 类的一个实例对象。

在类中,我们可以定义属性和方法。属性是对象的特征。例如,这可以是员工的薪水。方法定义我们可以对我们的对象执行的操作。方法可以定义一个取消账户的操作。从技术上讲,属性是类中定义的变量,方法是类中定义的函数。

Python 对象初始化

一个名为 __init__ 的特殊方法用于初始化对象。

object_initialization.py
#!/usr/bin/env python

# object_initialization.py

class Being:

    def __init__(self):
        print("Being is initialized")

Being()

我们有一个 Being 类。在对象创建后,名为 __init__ 的特殊方法会被自动调用。

$ ./object_initialization.py 
Being is initialized

Python 对象属性

属性是对象的特征。属性在 __init__ 方法中设置。

attributes.py
#!/usr/bin/env python

# attributes.py

class Cat:

    def __init__(self, name):

        self.name = name

missy = Cat('Missy')
lucky = Cat('Lucky')

print(missy.name)
print(lucky.name)

在这个代码示例中,我们有一个 Cat 类。在对象创建后,名为 __init__ 的特殊方法会被自动调用。

def __init__(self, name):

类定义中的每个方法都以对实例对象的引用开始。按照惯例,它被命名为 selfself 名称没有什么特别的。例如,我们可以将其命名为 this。第二个参数 name 是参数。值在类初始化期间传递。

self.name = name

这里我们将一个属性赋给一个实例对象。

missy = Cat('Missy')
lucky = Cat('Lucky')

这里我们创建了两个对象:猫 Missy 和 Lucky。参数的数量必须与类定义中的 __init__ 方法相对应。字符串 'Missy' 和 'Lucky' 成为 __init__ 方法的 name 参数。

print(missy.name)
print(lucky.name)

这里我们打印了两个猫对象的属性。类的每个实例都可以拥有自己的属性。

$ ./attributes.py 
Missy
Lucky

属性可以动态赋值,而不仅仅是在初始化期间。下一个例子证明了这一点。

attributes_dynamic.py
#!/usr/bin/env python

# attributes_dynamic.py

class Person:
    pass

p = Person()
p.age = 24
p.name = "Peter"

print("{0} is {1} years old".format(p.name, p.age))

我们定义并创建了一个空的 Person 类。

p.age = 24
p.name = "Peter"

这里我们动态创建了两个属性:age 和 name。

$ ./attributes_dynamic.py 
24 is Peter years old

Python 类属性

到目前为止,我们一直在讨论实例属性。在 Python 中,还有所谓的类对象属性。类对象属性对于类的所有实例都是相同的。

class_attribute.py
#!/usr/bin/env python

# class_attribute.py

class Cat:
    species = 'mammal'

    def __init__(self, name, age):

        self.name = name
        self.age = age


missy = Cat('Missy', 3)
lucky = Cat('Lucky', 5)

print(missy.name, missy.age)
print(lucky.name, lucky.age)

print(Cat.species)
print(missy.__class__.species)
print(lucky.__class__.species)

在我们的例子中,我们有两个猫,它们具有特定的 nameage 属性。这两只猫都共享一些特征。Missy 和 Lucky 都是哺乳动物。这反映在类级别的属性 species 中。该属性在类正文中任何方法名称之外定义。

print(Cat.species)
print(missy.__class__.species)

我们有两种方式可以访问类对象属性:一种是通过 Cat 类的名称,另一种是借助特殊的 __class__ 属性。

$ ./class_attribute.py 
Missy 3
Lucky 5
mammal
mammal
mammal

Python 方法

方法是在类正文中定义的一些函数。它们用于对我们对象的属性执行操作。方法在 OOP 范式的封装概念中至关重要。例如,我们的 AccessDatabase 类可能有一个 connect 方法。我们不需要知道 connect 方法确切地如何连接到数据库。我们只知道它用于连接到数据库。这对于划分编程职责至关重要,尤其是在大型应用程序中。

methods.py
#!/usr/bin/env python

# methods.py

class Circle:

    pi = 3.141592

    def __init__(self, radius=1):
        self.radius = radius

    def area(self):
        return self.radius * self.radius * Circle.pi

    def setRadius(self, radius):
        self.radius = radius

    def getRadius(self):
        return self.radius


c = Circle()

c.setRadius(5)
print(c.getRadius())
print(c.area())

在代码示例中,我们有一个 Circle 类。我们定义了三个新方法。

def area(self):
    return self.radius * self.radius * Circle.pi

area 方法返回圆的面积。

def setRadius(self, radius):
    self.radius = radius

setRadius 方法为 radius 属性设置新值。

def getRadius(self):
    return self.radius

getRadius 方法返回当前半径。

c.setRadius(5)

该方法在一个实例对象上调用。c 对象与类定义中的 self 参数配对。数字 5 与 radius 参数配对。

$ ./methods.py 
5
78.5398

在 Python 中,我们可以通过两种方式调用方法。有绑定非绑定方法调用。

bound_unbound_methods.py
#!/usr/bin/env python

# bound_unbound_methods.py

class Methods:

    def __init__(self):
        self.name = 'Methods'

    def getName(self):
        return self.name


m = Methods()

print(m.getName())
print(Methods.getName(m))

在这个例子中,我们演示了两种方法调用。

print(m.getName())

这是绑定方法调用。Python 解释器会自动将 m 实例与 self 参数配对。

print(Methods.getName(m))

这是非绑定方法调用。实例对象被显式地传递给 getName 方法。

$ ./bound_unbound_methods.py 
Methods
Methods

Python 继承

继承是利用已定义的类来形成新类的一种方式。新形成的类称为派生类,我们从中派生的类称为类。继承的重要好处是代码重用和降低程序复杂性。派生类(子类)重写或扩展基类(父类)的功能。

inheritance.py
#!/usr/bin/env python

# inheritance.py

class Animal:

    def __init__(self):
        print("Animal created")

    def whoAmI(self):
        print("Animal")

    def eat(self):
        print("Eating")


class Dog(Animal):

    def __init__(self):
        super().__init__()
        
        print("Dog created")

    def whoAmI(self):
        print("Dog")

    def bark(self):
        print("Woof!")

d = Dog()
d.whoAmI()
d.eat()
d.bark()

在这个例子中,我们有两个类:AnimalDogAnimal 是基类,Dog 是派生类。派生类继承基类的功能。这由 eat 方法显示。派生类修改了基类现有的行为,由 whoAmI 方法显示。最后,派生类通过定义新的 bark 方法来扩展基类的功能。

class Dog(Animal):

    def __init__(self):
        super().__init__()
        
        print("Dog created")

我们将祖先类放在子类名称后面的圆括号中。如果派生类提供了自己的 __init__ 方法并且我们想调用父构造函数,我们必须使用 super 函数显式调用基类的 __init__ 方法。

$ ./inherit.py 
Animal created
Dog created
Dog
Eating
Woof!

Python 多态

多态是通过对不同的数据输入以不同的方式使用运算符或函数的过程。在实际中,多态意味着如果类 B 继承自类 A,它不必继承类 A 的所有内容;它可以以不同的方式执行类 A 的某些操作。

basic_polymorphism.py
#!/usr/bin/env python

# basic_polymorphism.py

a = "alfa"
b = (1, 2, 3, 4)
c = ['o', 'm', 'e', 'g', 'a']

print(a[2])
print(b[1])
print(c[3])

Python 在内置类型中广泛使用多态。这里我们对三种不同的数据类型使用了相同的索引运算符。

$ ./basic_polymorphism.py 
f
2
g

多态主要用于处理继承。

polymorphism.py
#!/usr/bin/env python

# polymorphism.py

class Animal:
    
   def __init__(self, name=''):
       
      self.name = name

   def talk(self):
       
      pass

class Cat(Animal):
    
   def talk(self):
       
      print("Meow!")

class Dog(Animal):
    
   def talk(self):
       
      print("Woof!")

a = Animal()
a.talk()

c = Cat("Missy")
c.talk()

d = Dog("Rocky")
d.talk()

这里我们有两个物种:狗和猫。它们都是动物。Dog 类和 Cat 类继承自 Animal 类。它们有一个 talk 方法,该方法为它们产生不同的输出。

$ ./polymorphism.py 
Meow!
Woof!

Python 特殊方法

Python 编程语言中的类可以使用特殊的方法名称来实现某些操作。这些方法不是直接调用的,而是通过特定的语言语法调用的。这类似于 C++ 或 Ruby 中已知的运算符重载

special_methods.py
#!/usr/bin/env python

# special_methods.py

class Book:

    def __init__(self, title, author, pages):

        print("A book is created")

        self.title = title
        self.author = author
        self.pages = pages

    def __str__(self):

        return "Title:{0} , author:{1}, pages:{2} ".format(
            self.title, self.author, self.pages)

    def __len__(self):

        return self.pages

    def __del__(self):

        print("A book is destroyed")

book = Book("Inside Steve's Brain", "Leander Kahney", 304)

print(book)
print(len(book))
del book

在我们的代码示例中,我们有一个 book 类。这里我们引入了四个特殊方法:__init____str____len____del__

book = Book("Inside Steve's Brain", "Leander Kahney", 304)

这里我们调用了 __init__ 方法。该方法创建了一个 Book 类的新实例。

print(book)

print 函数调用 __str__ 方法。此方法应返回对象的非正式字符串表示。

print(len(book))

len 函数调用 __len__ 方法。在本例中,我们打印了我们书的页数。

del book

del 关键字删除一个对象。它调用其 __del__ 方法。

在下一个例子中,我们将实现一个 vector 类并演示对其进行加法和减法运算。

vector.py
#!/usr/bin/env python

# vector.py

class Vector:

    def __init__(self, data):

        self.data = data

    def __str__(self):

        return repr(self.data)

    def __add__(self, other):

        data = []

        for j in range(len(self.data)):

            data.append(self.data[j] + other.data[j])

        return Vector(data)

    def __sub__(self, other):

        data = []

        for j in range(len(self.data)):

            data.append(self.data[j] - other.data[j])

        return Vector(data)


x = Vector([1, 2, 3])
y = Vector([3, 0, 2])

print(x + y)
print(y - x)

该示例展示了 __add____sub__ 方法。

def __add__(self, other):

    data = []

    for j in range(len(self.data)):

        data.append(self.data[j] + other.data[j])

    return Vector(data)

这里我们实现了向量的加法运算。当我们将两个 Vector 对象与 + 运算符相加时,会调用 __add__ 方法。这里我们对相应向量的每个成员进行相加。

$ ./vector.py 
[4, 2, 5]
[2, -2, -1]

在本节 Python 教程中,我们介绍了 Python 中的面向对象编程。