文章目录
  1. 1. 概述
  2. 2. 面向对象基础
    1. 2.1. 构造函数
    2. 2.2. 实例属性
    3. 2.3. 类属性
      1. 2.3.1. 通过类名修改类属性
      2. 2.3.2. 通过实例对象修改类属性
    4. 2.4. 类方法
  3. 3. 面向对象之封装
    1. 3.1. public成员
    2. 3.2. protected成员
    3. 3.3. private成员
  4. 4. 面向对象之继承
    1. 4.1. 继承的基本使用
    2. 4.2. 覆盖父类方法
    3. 4.3. super()函数
    4. 4.4. 多重继承
  5. 5. 总结
  6. 6. 参考

概述

Python 是一门完全面向对象的语言。在 Python 中,一切都是对象。从我们开始使用 Python 时,我们就已经使用了 Python 中的对象和类。
Python 程序中的每一个元素都是类的一个实例,如数字、字符串、列表、字典这些元素,都是 Python 内置类对应的一个对象,甚至于使用def关键字定义的一个函数都对应一个function类。

面向对象基础

Python 使用class关键字声明一个类。

一个典型的 Python 类主要包含以下成员:

  • 构造函数 (constructor)
  • 实例属性 (Instance Attributes)
  • 类属性 (Class Attributes)
  • 方法 (Class Attributes)

一个类也可以没有任何成员,如下面的Car类就没有任何成员:

1
2
3
class Car:
'''This is an empty Car Class'''
pass

下面的类则包含了实例属性、类属性、构造函数、方法等成员:

1
2
3
4
5
6
7
8
9
class Car:
brand = 'BenChi'

def __init__(self):
self.price = 10000
self.chair = 7

def run(self):
print('Run 100km Per Hour....')

构造函数

在 Python 中,构造函数是实例化一个类对象的时自动被调用的函数,类似于 Java 或 C# 中的构造函数。构造函数必须使用一个特定的名称__init__,并且必须有一个固定的参数self

Python 类中几乎每一个方法都要将 self 作为第一个参数,self 是调用该方法的对象的引用。其实,第一个参数的名字是任意的,并不一定要叫 self, self 只是一种约定。

注意,注意,注意: __init__方法并不是 Python 类构造函数,关于 Python 类的构造函数其实有很大争议,这里主要是为了介绍面向对象的概念。

下面的例子定义了一个构造函数:

1
2
3
4
5
6
7
class Car:
def __init__(self):
print('Call Constructor......')


car1 = Car()
car2 = Car()

Python 中构造函数(init)的作用定义了类实例的属性,并对实例属性进行复制。

实例属性

实例属性是与类实例绑定的属性,其定义在构造函数(init)中。

下面的例子定义了两个实例属性:

1
2
3
4
class Car:
def __init__(self):
self.price = 100
self.chair = 7

类属性

类属性不同于实例属性,类属性的值被所有实例对象共享。类属性在类级别定义而不是定义在__init__方法中。类属性的访问也不同于实例属性,类属性的访问是通过类名, 也可以通过实例访问。

下面例子定义了一个类属性:

1
2
3
4
5
class Car:
brand = "benchi"


print(Car.brand)

通过类名修改类属性

通过类名修改类属性,那么该类所有的实例对象对应的类属性都会被修改, 如下:

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
class Car:
brand = "benchi"


car1 = Car()
car2 = Car()

print('Before Change.............')
print(f'car1 brand is: {car1.brand}')
print(f'car2 brand is: {car2.brand}')

print('Change Class Attribute By [Class Name]')
Car.brand = 'Audi'

print('After Change.............')
print(f'car1 brand is: {car1.brand}')
print(f'car2 brand is: {car2.brand}')

输出:
Before Change.............
car1 brand is: benchi
car2 brand is: benchi
Change Class Attribute By [Class Name]
After Change.............
car1 brand is: Audi
car2 brand is: Audi

通过实例对象修改类属性

通过实例对象修改类属性,并不会影响其他对象类属性的值,他仅仅影响该实例对象自己类属性的值,如下:

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
class Car:
brand = "benchi"


car1 = Car()
car2 = Car()

print('Before Change.............')
print(f'car1 brand is: {car1.brand}')
print(f'car2 brand is: {car2.brand}')

print('Change Class Attribute By [Object]')
car1.brand = 'Audi'
car2.brand = 'KaYan'

print('After Change.............')
print(f'car1 brand is: {car1.brand}')
print(f'car2 brand is: {car2.brand}')
print(f'Car brand is: {Car.brand}')

输出:

Before Change.............
car1 brand is: benchi
car2 brand is: benchi
Change Class Attribute By [Object]
After Change.............
car1 brand is: Audi
car2 brand is: KaYan
Car brand is: benchi

类方法

在一个类中,可以定义尽可能多的类方法,一般情况下,类方法的第一个参数都是self

下面的类就定义了一个类方法:

1
2
3
4
5
class Car:
brand = "benchi"

def run(self):
print('Run......')

面向对象之封装

面向对象语言,如Java、C++等,使用publicprotectedprivate等关键字来控制对类资源的访问。

  • 类的private成员仅能在类内部进行访问,其他任何地方都不能访问,包括其子类。

  • 类的protected成员可以在类内部和其子类中访问。

  • 类的public成员可以在任何地方进行访问。

Python没有使用关键字机制来限制对类成员方法和类成员属性的访问,Python规定了一种约定,在类方法或者类成员前加_(下划线)或者__(双下划线)来模拟private或者public访问修饰期的功能。

public成员

Python中的所有成员默认都是public属性,可以在任何地方访问。

如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Car:
def __init__(self, brand, price):
self.brand = brand
self.price = price

def run(self):
print('Run......')


brand = 'Audi'
price = 10000
car = Car(brand, price)

# 访问类属性
print(car.brand)
print(car.price)

# 访问类方法
car.run()

输出结果:
Audi
10000
Run......

protected成员

Python约定,如果类成员前加了单个下划线_, 则类成员为protected成员。

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
class Car:
def __init__(self, brand, price):
self._brand = brand
self._price = price

def _run(self):
print('Run......')


brand = 'Audi'
price = 10000
car = Car(brand, price)

# 访问protected属性
print(car._brand)
print(car._price)

# 修改protected属性
car._price = 20000
print(car._price)

# 访问类方法
car._run()

输出结果:
Audi
10000
20000
Run......

使用单下划线的方式将属性声明为protected, 并不能真正起到作用,它仅仅是一种约定。

private成员

Python约定,如果类成员前加了双下划线__, 则类成员为private成员。

如下代码:

1
2
3
4
5
6
7
8
9
10
11
class Car:
def __init__(self, brand, price):
self._brand = brand
self._price = price

def __run(self):
print('Run......')


# 访问类方法
car._run() # 程序报错,'Car' object has no attribute '_run'

面向对象之继承

在面向对象编程中,继承是达到代码重用目的的一种方式。在继承中,子类可以使用其父类的一部分代码。

继承的基本使用

下面先创建一个父类, 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Fish:
def __init__(self, first_name, last_name="Fish",
skeleton="bone", eyelids=False):
self.first_name = first_name
self.last_name = last_name
self.skeleton = skeleton
self.eyelids = eyelids

def swim(self):
print("The fish is swimming.")

def swim_backwards(self):
print("The fish can swim backwards.")

接下来,创建一个没有任何方法和属性的子类,代码如下:

1
2
class Trout(Fish):
pass

Python类继承的语法是在定义类的时候,传入父类的名字作为参数

由于Trout类继承了Fish类,Trout类就是Fish类的子类,Trout类的对象就可以调用其父类的所有方法,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
terry = Trout("Terry")
print(terry.first_name + " " + terry.last_name)
print(terry.skeleton)
print(terry.eyelids)
terry.swim()
terry.swim_backwards()

输出:

Terry Fish
bone
False
The fish is swimming.
The fish can swim backwards.

接下来,再创建另一个子类,该子类有自己的方法:

1
2
3
4
class Clownfish(Fish):

def live_with_anemone(self):
print("The clownfish is coexisting with sea anemone.")

创建Clownfish的一个对象,并调用其方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Clownfish(Fish):

def live_with_anemone(self):
print("The clownfish is coexisting with sea anemone.")

casey = Clownfish("Casey")
print(casey.first_name + " " + casey.last_name)
casey.swim()
casey.live_with_anemone()

输出:
Casey Fish
The fish is swimming.
The clownfish is coexisting with sea anemone.

从输出结果可以看出,Clownfish的对象casey可以调用其父类的swim()方法和__init__()方法,同时可以调用它自身的live_with_anemone()方法。

覆盖父类方法

有时候,子类继承了父类的全部方法和属性,但是只想使用其中的一部分方法和属性,当子类定义了和父类一样的方法时,就会覆盖(override)父类的方法。

有一点需要注意的是,尽管子类覆盖了父类的方法,但是并没有产生多余的代码。

下面定义一个覆盖父类__init__()方法和swim_backwards()方法的类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Shark(Fish):
def __init__(self, first_name, last_name="Shark",
skeleton="cartilage", eyelids=True):
self.first_name = first_name
self.last_name = last_name
self.skeleton = skeleton
self.eyelids = eyelids

def swim_backwards(self):
print("The shark cannot swim backwards, but can sink backwards.")

sammy = Shark("Sammy")
print(sammy.first_name + " " + sammy.last_name)
sammy.swim()
sammy.swim_backwards()
print(sammy.eyelids)
print(sammy.skeleton)

结果:
Sammy Shark
The fish is swimming.
The shark cannot swim backwards, but can sink backwards.
True
cartilage

从输出结果可以看出,子类成功覆盖了父类的方法。

super()函数

如果子类覆盖了父类的方法,同时又想使用父类的方法,可以使用super()函数。

super()函数经常在__init__()方法中被使用。

修改一下上文所提到的Trout类,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Trout(Fish):
def __init__(self, water = "freshwater"):
self.water = water
super().__init__(self)


terry = Trout()

terry.first_name = "Terry"

print(terry.first_name + " " + terry.last_name)
print(terry.eyelids)

print(terry.water)

terry.swim()

输出:

Terry Fish
False
freshwater
The fish is swimming.

从输出结果可以看出,使用super()函数成功调用了父类被子类覆盖的方法。

多重继承

多重继承即一个类可以继承多个类。

多重继承基本语法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Coral:

def community(self):
print("Coral lives in a community.")


class Anemone:

def protect_clownfish(self):
print("The anemone is protecting the clownfish.")


class CoralReef(Coral, Anemone):
pass

总结

本篇文章总结了Python面向对象的基本概念及基本使用方法,弥补自己在Python方面面向对象方法的不足。

参考