OOP基本特性

文章发布时间:

最后更新时间:

文章总字数:
1.9k

预计阅读时间:
8 分钟

一.封装

  • 封装是指将对象的属性和方法包装在一起,对外隐藏实现细节,只提供必要的接口给外部访问。

  • 在Python中,通过 init 方法初始化属性,并通过方法来操作这些属性。

  • 以 __ 开头的属性或方法是私有的,在子类和类外部无法直接使用

  • 可使用私有属性和公有方法控制外部访问。

二.继承/派生

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 Ganyuan:
def __init__(self, name, atk):
self.name = name
self.atk = atk
def ATK(self):
print('%s的攻击力是%d'%(self.name, self.atk))


class jinwei(Ganyuan):
def __init__(self, name, atk,fy):
# 调用父类的初始化方法,通过supper设置,这里super括号中其实是默认的(当前类,self),所以不用写self
super().__init__(name, atk)
self.fy = fy

def FY(self):
print('%s的防御力是%d'%(self.name, self.fy))

Ace = jinwei('Ace', 100, 200)

# 父类的方法无需在子类中提及,子类中找不到就会去父类找
Ace.ATK()
Ace.FY()

输出:

1
2
Ace的攻击力是100
Ace的防御力是200
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
# 多继承
class Ganyuan:
def __init__(self, name, atk):
self.name = name
self.atk = atk
def ATK(self):
print('%s的攻击力是%d'%(self.name,self.atk))


class man:
def __init__(self, age):
self.age = age
def AGE(self):
print('年龄是%d'%(self.age))

class jinwei(Ganyuan,man):

def FY(self):
print('%s的防御力是%d'%(self.name, self.fy))

# 多继承也要把所有的属性写出来
def __init__(self,name,atk,age,fy):
# 不同的是不用super(),而直接用名字,且括号中要加入self
Ganyuan.__init__(self,name,atk)
man.__init__(self,age)
self.fy = fy



Ace = jinwei('Ace',100,200,10)
Ace.ATK()
Ace.FY()
Ace.AGE()

输出:

1
2
3
Ace的攻击力是100
Ace的防御力是10
年龄是200

三.覆盖 – 重写

  • 在子类中实现与基类同名的方法,我们叫覆盖。

  • 实现和父类同名,但功能不同的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 父类
class father:
def __init__(self,name,age):
self.name = name
self.age = age
def show(self):
print('我是%s,今年%d岁'%(self.name, self.age))

# 子类
class son(father):
# 这个子类有什么属性,init方法里一定要写出来
def __init__(self,name,age,atk):
# 调用父类的init方法时,小括号里填父类有的
super().__init__(name,age)
self.atk = atk


a = son('Ace', 18, 100)
a.show()

输出:

1
我是Ace,今年18岁

四.多态

  • 多态是指同一个方法在不同对象上具有不同的行为

  • 多态的实现通常通过继承和方法重写来实现

  • 子类创建的对象既属于子类也属于父类

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
36
class Animal:
def speak(self):
print('Animal speak')

class Dog(Animal):
# 相同的方法,不同的表现,重写了父类方法
def speak(self):
print('Dog speak')

class Cat(Animal):
# 相同的方法,不同的表现,重写了父类方法
def speak(self):
print('Cat speak')

# 可以看到这里没用重写,直接继承了父类speak方法
class bird(Animal):
def fly(self):
print('bird fly')

def animal_speak(animal):

# 判断animal是不是Animal的实例
if isinstance(animal, Animal):
animal.speak()
else:
print('animal is not Animal')

dog = Dog()
cat = Cat()
Bird = bird()

animal_speak(dog)
animal_speak(cat)

# 由于继承了父类方法,所以不管怎么样都不会报错
animal_speak(Bird)

输出:

1
2
3
Dog speak
Cat speak
Animal speak

五.对象对成员的操作

1
2
3
4
5
6
7
8
9
10
class arknights:
x = 10
atk = 100

Ace = arknights()
# 打印已有的数据
print(Ace.x)

# 访问没有的属性
print(Ace.fy)

输出:

1
2
3
4
5
6
7
8
9
10
10

---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[6], line 10
7 print(Ace.x)
9 # 访问没有的属性
---> 10 print(Ace.fy)

AttributeError: 'arknights' object has no attribute 'fy'

可以看到一个对象访问其中没有的属性是会报错的,但对象可以直接添加一个属性,这样不会报错。

1
2
3
4
5
6
7
8
9
class arknights:
x = 10
atk = 100

Ace = arknights()
# 直接添加一个属性并赋值
Ace.fy = 200
print(Ace.fy)
print(arknights.fy)

输出:

1
2
3
4
5
6
7
8
9
10
200

---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[9], line 9
7 Ace.fy = 200
8 print(Ace.fy)
----> 9 print(arknights.fy)

AttributeError: type object 'arknights' has no attribute 'fy'

事实证明一个对象确实可以拥有自己的属性,原类中没有且不能访问。

六.私有属性

用 __x 来表示私有属性,私有属性是跟对象有关的,也是类属性的一种。

1
2
3
4
5
class A:
__x = 1

a = A()
print(a.__x)

输出:

1
2
3
4
5
6
7
8
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[13], line 5
2 __x = 1
4 a = A()
----> 5 print(a.__x)

AttributeError: 'A' object has no attribute '__x'

这个类创建的对象是不能直接访问类的私有属性的,那该怎么访问呢?可以在父类中定义一个方法,这个方法在类中是可以访问私有属性的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class A:
__x = 1

# 定义访问私有属性的方法,实际也是闭包思想
def fun(self):
self.__x = 2
def fun1(self):
print(self.__x)

a = A()
b = A()
# 更改a对象的私有属性
a.fun()
a.fun1()
# b只能访问自己的私有属性,a只能通过类更改自己的私有属性
b.fun1()
# 同理类也可以访问自己的私有属性
A.fun1(A)

输出:

1
2
3
2
1
1

这也就反映了为什么说私有属性是类属性的一种,因为私有属性像类属性一样会继承给对象,而对象更改私有属性也只会更改自己的私有属性,而不会影响其他对象。

七.重载

实际上就是把原有的魔术方法更改了,这里不提前面写过的方法,提一些其他的方法:

  • **mul(self, rhs) **计算乘法

  • **truediv(self, rhs) **计算除法

  • **floordiv(self, rhs) **计算整除

  • **mod(self, rhs) **计算取余

  • pow(self, rhs) 计算幂

八.super函数

  • 用于调用父类(超类)的一个方法

  • 一般来说方法是直接继承的不需要用super()

  • 但又想调用父类方法,又想重写一部分也可以

  • super(当前类,self)是python的标准写法,而python3.x中, 其可以自动推论,所以类内可以直接写super()

  来一个很好的比喻,父类A中的fun()方法有10万行代码,管理者觉得非常好。继承A的B类中的fun()方法只有1万行代码,管理者觉得也很好。现在希望把这11万行代码一起运行,那么,A和B的fun()方法应该如何组合呢?难道要把那10万行代码又重新写一遍再加上1万行?那么我们现在就来看看。

1
2
3
4
5
6
7
8
9
10
11
12
13
class A:
def fun(self):
print('A')

class B(A):
# 重写时,要调用父类的方法,用super()
def fun(self):
# 这里因为先写super(),所以先调用父类的fun方法
super().fun()
print('B')

b = B()
b.fun()

输出:

1
2
A
B

注意,super()不一定非得写在类的函数里面,也可以直接在外部直接使用

1
2
3
4
5
6
7
8
9
10
11
12
class A:
def fun(self):
print('A')

class B(A):
# 重写时,要调用父类的方法,用super()
def fun(self):
print('B')

b = B()
# 里面先写类再写对象,表示调用的是B的父类的fun方法,且是对象b来操作
super(B,b).fun()

输出:

1
A

关于这个使用方法,可以出一道思考题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class A:
x = 1
def fun(self):
print('A')
print(self.x)

class B(A):
x = 2
def fun(self):
print('B')
print(self.x)

b = B()
b.x = 3
# 猜猜最后的结果是什么
super(B,b).fun()

输出:

1
2
A
3

结果显而易见,调用了父类A的fun()方法,但其传进去的参数却是子类B创建的b对象,因此打印了A中的语句,以及b对象的属性。