原文: http://zetcode.com/lang/python/oop/

在 Python 教程的这一部分中,我们将讨论 Python 中的面向对象编程。

那里有三种广泛使用的编程示例:过程编程,函数编程和面向对象的编程。 Python 支持所有三种编程示例。

面向对象编程

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

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

  • 抽象
  • 多态
  • 封装
  • 继承

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

Python 对象

Python 中的所有内容都是一个对象。 对象是 Python OOP 程序的基本构建块。

object_types.py

  1. #!/usr/bin/env python
  2. # object_types.py
  3. import sys
  4. def function():
  5. pass
  6. print(type(1))
  7. print(type(""))
  8. print(type([]))
  9. print(type({}))
  10. print(type(()))
  11. print(type(object))
  12. print(type(function))
  13. print(type(sys))

在此示例中,我们显示所有这些实体实际上都是对象。 type()函数返回指定对象的类型。

  1. $ ./object_types.py
  2. <class 'int'>
  3. <class 'str'>
  4. <class 'list'>
  5. <class 'dict'>
  6. <class 'tuple'>
  7. <class 'type'>
  8. <class 'function'>
  9. <class 'module'>

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

Python class关键字

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

first_object.py

  1. #!/usr/bin/env python
  2. # first_object.py
  3. class First:
  4. pass
  5. fr = First()
  6. print(type(fr))
  7. print(type(First))

这是我们的头等舱。 该类的主体暂时留空。 按照惯例,给类起一个以大写字母开头的名称。

  1. class First:
  2. pass

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

  1. fr = First()

在这里,我们创建First类的新实例。 换句话说,我们实例化了First类。 fr是对我们新对象的引用。

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

在这里,我们看到frFirst类的实例对象。

在类内部,我们可以定义属性和方法。 属性是对象的特征。 例如,这可以是雇员的工资。 方法定义了我们可以对对象执行的操作。 一种方法可以定义取消帐户。 从技术上讲,属性是变量,方法是在类内部定义的函数。

Python 对象初始化

称为__init__()的特殊方法用于初始化对象。

object_initialization.py

  1. #!/usr/bin/env python
  2. # object_initialization.py
  3. class Being:
  4. def __init__(self):
  5. print("Being is initialized")
  6. Being()

我们有一个Being类。 创建对象后立即自动调用特殊方法__init__()

  1. $ ./object_initialization.py
  2. Being is initialized

这是示例输出。

Python 对象属性

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

attributes.py

  1. #!/usr/bin/env python
  2. # attributes.py
  3. class Cat:
  4. def __init__(self, name):
  5. self.name = name
  6. missy = Cat('Missy')
  7. lucky = Cat('Lucky')
  8. print(missy.name)
  9. print(lucky.name)

在此代码示例中,我们有一个Cat类。 创建对象后立即自动调用特殊方法__init__()

  1. def __init__(self, name):

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

  1. self.name = name

在这里,我们将属性传递给实例对象。

  1. missy = Cat('Missy')
  2. lucky = Cat('Lucky')

在这里,我们创建两个对象:猫MissyLucky。 参数的数量必须与类定义的__init__()方法相对应。 "Missy""Lucky"字符串成为__init__()方法的name参数。

  1. print(missy.name)
  2. print(lucky.name)

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

  1. $ ./attributes.py
  2. Missy
  3. Lucky

可以动态分配属性,而不仅仅是在初始化过程中。 下一个示例对此进行了说明。

attributes_dynamic.py

  1. #!/usr/bin/env python
  2. # attributes_dynamic.py
  3. class Person:
  4. pass
  5. p = Person()
  6. p.age = 24
  7. p.name = "Peter"
  8. print("{0} is {1} years old".format(p.name, p.age))

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

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

在这里,我们动态创建两个属性:agename

  1. $ ./attributes_dynamic.py
  2. 24 is Peter years old

Python 类属性

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

class_attribute.py

  1. #!/usr/bin/env python
  2. # class_attribute.py
  3. class Cat:
  4. species = 'mammal'
  5. def __init__(self, name, age):
  6. self.name = name
  7. self.age = age
  8. missy = Cat('Missy', 3)
  9. lucky = Cat('Lucky', 5)
  10. print(missy.name, missy.age)
  11. print(lucky.name, lucky.age)
  12. print(Cat.species)
  13. print(missy.__class__.species)
  14. print(lucky.__class__.species)

在我们的示例中,我们有两只具有特定nameage属性的猫。 两只猫都有一些共同之处。 小姐和幸运者都是哺乳动物。 这反映在类级别属性species中。 该属性是在类主体中的任何方法名称之外定义的。

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

有两种方法可以访问类对象属性:通过Cat类的名称,或借助特殊的__class__属性。

  1. $ ./class_attribute.py
  2. Missy 3
  3. Lucky 5
  4. mammal
  5. mammal
  6. mammal

Python 方法

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

methods.py

  1. #!/usr/bin/env python
  2. # methods.py
  3. class Circle:
  4. pi = 3.141592
  5. def __init__(self, radius=1):
  6. self.radius = radius
  7. def area(self):
  8. return self.radius * self.radius * Circle.pi
  9. def setRadius(self, radius):
  10. self.radius = radius
  11. def getRadius(self):
  12. return self.radius
  13. c = Circle()
  14. c.setRadius(5)
  15. print(c.getRadius())
  16. print(c.area())

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

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

area()方法返回圆的面积。

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

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

  1. def getRadius(self):
  2. return self.radius

getRadius()方法返回当前半径。

  1. c.setRadius(5)

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

  1. $ ./methods.py
  2. 5
  3. 78.5398

在 Python 中,我们可以通过两种方式调用方法。 有有界的和无界的方法调用。

bound_unbound_methods.py

  1. #!/usr/bin/env python
  2. # bound_unbound_methods.py
  3. class Methods:
  4. def __init__(self):
  5. self.name = 'Methods'
  6. def getName(self):
  7. return self.name
  8. m = Methods()
  9. print(m.getName())
  10. print(Methods.getName(m))

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

  1. print(m.getName())

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

  1. print(Methods.getName(m))

这是无界的方法调用。 实例对象已显式提供给getName()方法。

  1. $ ./bound_unbound_methods.py
  2. Methods
  3. Methods

Python 继承

继承是一种使用已经定义的类形成新类的方法。 新形成的类称为派生的类,我们从中衍生的类称为基类。 继承的重要好处是代码重用和降低程序的复杂性。 派生类(后代)将覆盖或扩展基类(祖先)的功能。

inheritance.py

  1. #!/usr/bin/env python
  2. # inheritance.py
  3. class Animal:
  4. def __init__(self):
  5. print("Animal created")
  6. def whoAmI(self):
  7. print("Animal")
  8. def eat(self):
  9. print("Eating")
  10. class Dog(Animal):
  11. def __init__(self):
  12. super().__init__()
  13. print("Dog created")
  14. def whoAmI(self):
  15. print("Dog")
  16. def bark(self):
  17. print("Woof!")
  18. d = Dog()
  19. d.whoAmI()
  20. d.eat()
  21. d.bark()

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

  1. class Dog(Animal):
  2. def __init__(self):
  3. super().__init__()
  4. print("Dog created")

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

  1. $ ./inherit.py
  2. Animal created
  3. Dog created
  4. Dog
  5. Eating
  6. Woof!

Python 多态

多态是对不同的数据输入以不同方式使用运算符或函数的过程。 实际上,多态意味着如果类 B 从类 A 继承,则不必继承关于类 A 的所有内容; 它可以完成 A 类所做的某些事情。

basic_polymorphism.py

  1. #!/usr/bin/env python
  2. # basic_polymorphism.py
  3. a = "alfa"
  4. b = (1, 2, 3, 4)
  5. c = ['o', 'm', 'e', 'g', 'a']
  6. print(a[2])
  7. print(b[1])
  8. print(c[3])

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

  1. $ ./basic_polymorphism.py
  2. f
  3. 2
  4. g

多态在处理继承时最常用。

polymorphism.py

  1. #!/usr/bin/env python
  2. # polymorphism.py
  3. class Animal:
  4. def __init__(self, name=''):
  5. self.name = name
  6. def talk(self):
  7. pass
  8. class Cat(Animal):
  9. def talk(self):
  10. print("Meow!")
  11. class Dog(Animal):
  12. def talk(self):
  13. print("Woof!")
  14. a = Animal()
  15. a.talk()
  16. c = Cat("Missy")
  17. c.talk()
  18. d = Dog("Rocky")
  19. d.talk()

在这里,我们有两种:狗和猫。 两者都是动物。 Dog类和Cat类继承了Animal类。 它们具有talk()方法,可以为它们提供不同的输出。

  1. $ ./polymorphism.py
  2. Meow!
  3. Woof!

Python 特殊方法

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

special_methods.py

  1. #!/usr/bin/env python
  2. # special_methods.py
  3. class Book:
  4. def __init__(self, title, author, pages):
  5. print("A book is created")
  6. self.title = title
  7. self.author = author
  8. self.pages = pages
  9. def __str__(self):
  10. return "Title:{0} , author:{1}, pages:{2} ".format(
  11. self.title, self.author, self.pages)
  12. def __len__(self):
  13. return self.pages
  14. def __del__(self):
  15. print("A book is destroyed")
  16. book = Book("Inside Steve's Brain", "Leander Kahney", 304)
  17. print(book)
  18. print(len(book))
  19. del book

在我们的代码示例中,我们有一个book类。 在这里,我们介绍四种特殊方法:__init__()__str__()__len__()__del__()

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

在这里,我们称为__init__()方法。 该方法创建Book类的新实例。

  1. print(book)

print关键字调用__str__()方法。 此方法应返回对象的非正式字符串表示形式。

  1. print(len(book))

len()函数调用__len__()方法。 就我们而言,我们打印书的页数。

  1. del book

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

在下一个示例中,我们实现一个向量类并演示对其的加法和减法运算。

vector.py

  1. #!/usr/bin/env python
  2. # vector.py
  3. class Vector:
  4. def __init__(self, data):
  5. self.data = data
  6. def __str__(self):
  7. return repr(self.data)
  8. def __add__(self, other):
  9. data = []
  10. for j in range(len(self.data)):
  11. data.append(self.data[j] + other.data[j])
  12. return Vector(data)
  13. def __sub__(self, other):
  14. data = []
  15. for j in range(len(self.data)):
  16. data.append(self.data[j] - other.data[j])
  17. return Vector(data)
  18. x = Vector([1, 2, 3])
  19. y = Vector([3, 0, 2])
  20. print(x + y)
  21. print(y - x)

该示例介绍了__add____sub__方法。

  1. def __add__(self, other):
  2. data = []
  3. for j in range(len(self.data)):
  4. data.append(self.data[j] + other.data[j])
  5. return Vector(data)

在这里,我们实现向量的加法运算。 当我们使用+运算符添加两个Vector对象时,将调用__add__()方法。 在这里,我们将各个向量的每个成员相加。

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

这是输出。

在 Python 教程的这一部分中,我们介绍了 Python 中的面向对象编程。