面向对象

面向对象

1. 面向对象

面向对象都有两个基本概念, 分别是 对象

面向对象的三大特征:

  • 继承: 即一个派生类(derived class)继承基类(base class)的字段和方法。
    • 继承允许把一个派生类的对象作为一个基类对象对待。
    • 一个 Dog 类型的对象派生自 Animal 类
  • 多态: 指对不同类型的变量进行相同的操作,它会根据对象(或类)类型的不同而表现出不同的行为
  • 封装: 将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体(即类)
    • 封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,一特定的访问权限来使用类的成员。

2. 类的定义和调用

2.1 类的定义

语法格式:

1
2
class 类名():
...
1
2
3
4
5
class ClassA():
var1 = 100 # 类变量

def fun1(): # 类方法
print("hello fun1")

2.2 调用类属性和类方法

  • 类中的变量叫属性 —-> 调用格式: 类名.属性名
  • 类中的函数叫方法 —-> 调用格式: 类名.方法名()
1
2
3
4
5
6
7
8
class ClassA():
var1 = 100 # 类变量

def fun1(): # 类方法
print("hello fun1")

print(ClassA.var1) # 100
print(ClassA.fun1()) # hello fun1

3. 类方法

3.1 类方法调用类属性

1
2
3
4
5
6
7
8
class ClassA():
var1 = 'hello'

@classmethod
def fun1(cls):
print(f'{cls.var1} world')

ClassA.fun1() # hello world
  • @classmethod: 类方法装饰器, 用来声明类方法, 只有声明了类方法才能使用类属性
  • cls: 类的引用,指向类本身, 是类方法的第一个参数, 类似于self
  • 无论是@classmethod还是cls都不能省去, 否则会报错

3.2 类方法传参

1
2
3
4
5
6
7
8
class ClassA():
var1 = 'hello'

@classmethod
def fun1(cls, var2):
print(f'{cls.var1} {var2}')

ClassA.fun1('world') # hello world

4. 修改和增加类属性

4.1 从内部增加和修改类属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class ClassA:
var1 = '两点水'

@classmethod
def method1(cls):
print(f'原来的 var1 值为:{cls.var1}')
cls.var1 = '三点水'
print(f'修改后的 var1 值为:{cls.var1}')
cls.var2 = '新添加的类变量'
print(f'新添加的 var2 值为:{cls.var2}')

ClassA.method1()

输出结果为:
原来的 var1 值为:两点水
修改后的 var1 值为:三点水
新添加的 var2 值为:新添加的类变量

4.2 从外部增加和修改类属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class ClassA:
var1 = '两点水'

@classmethod
def fun1(cls):
print(f'var1 值为:{cls.var1}')


ClassA.fun1() # var1 值为:两点水
ClassA.var1 = 'twowater'
ClassA.fun1() # var1 值为:twowater

ClassA.var2 = '四点水'
print(ClassA.var2) # 四点水

5. 类和对象

5.1 类和对象的概念

类是对象的模板,对象是类的实例。

类相当于模具, 对象就是根据模具制造出来的产品
从模具变成产品的过程就是类的实例化, 类实例化之后就是对象

5.2 类的实例化

类的实例化和直接使用类的区别:

  • 类方法里面没有了@classmethod声明了,不用声明他是类方法
  • 类方法里面的参数cls改为self
  • 实例化对象 实例名 = 类(), 调用实例方法 实例名.函数(),使用 实例名.变量名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class ClassA:
var1 = '两点水'

@classmethod
def fun1(cls):
print(f'var1 值为:{cls.var1}')

class ClassB:
var2 = 'Python'

def fun2(self):
print(f'var2 值为:{self.var2}')


ClassA.fun1() # 使用类方法
ClassB().fun2() # 使用实例方法
  • clsself 是我们的编程规范, 并非不可更改
  • cls 是类方法中的第一个参数, self 是实例方法中的第一个参数
  • clsself 都是引用, cls 是类引用, self 是实例引用

5.3 实例属性和类属性

  • 修改类属性时, 实例属性也会被修改
  • 修改实例属性时, 类属性不会被修改
1
2
3
4
5
6
7
8
9
10
class ClassA:
var1 = '两点水'

def fun1(self):
print(f'var1 值为:{self.var1}')

a = ClassA()
a.var1 = 'twowater'
a.fun1() # var1 值为:twowater
print(ClassA.var1) # 两点水

5.4 实例方法和类方法

  • 类方法更改后, 实例方法也会更改, 相当于 类的重写
1
2
3
4
5
6
7
8
9
10
11
12
13
class ClassA:
var1 = '两点水'

def fun1(self):
print(f'第一次打印')

def new_fun1():
print(f'第二次打印')

a = ClassA()
a.fun1() # 第一次打印
a.fun1 = new_fun1
a.fun1() # 第二次打印

6. 初始化函数及析构函数

  • __init__(self): 初始化函数,也叫构造函数,类创建时自动执行
  • __del__(self): 析构函数,类销毁时自动执行
  • 初始化函数和析构函数的写法是固定的,第一个参数一定是 self
1
2
3
4
5
6
7
8
9
10
class ClassName:
def __init__(self):
print("实例化成功")

def __del__(self):
print("实例化销毁")


c1 = ClassName() # 实例化成功
del c1 # 实例化销毁

7. 类的继承

7.1 继承的概念

继承是面向对象编程中一个重要的概念,它允许我们定义一个类,该类可以继承另一个类,从而获得父类的方法和属性。
继承的类通常叫子类, 被继承的类通常叫父类, object是所有类的父类

基本语法:

1
2
class 子类(父类):
pass

多继承语法:

1
2
class 子类(父类1, 父类2, ...):
pass

多继承中,如果父类中有相同的方法名,而子类使用时未指定,则从左到右搜索父类方法,如果父类中没有该方法,则报错

继承子类的好处:

  • 会继承父类的方法和属性
  • 也可以重写父类方法

7.2 调用父类方法

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

def __init__(self, name, age, account):
self.name = name
self._age = age
self.__account = account

def get_account(self):
return self.__account

class UserInfo2(UserInfo):
pass

if __name__ == '__main__':
user_info_2 = UserInfo2('Tom', 18, '123456')
print(user_info_2.get_account()) # 123456

7.3 父类方法重写

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 UserInfo:
lv = 5

def __init__(self, name, age, account):
self.name = name
self._age = age
self.__account = account

def get_account(self):
return self.__account

@classmethod
def get_name(cls):
return cls.lv

@property
def get_age(self):
return self._age


class UserInfo2(UserInfo):
def __init__(self, name, age, account, sex):
super().__init__(name, age, account)
self.sex = sex


if __name__ == '__main__':
user_info_2 = UserInfo2('Tom', 18, '123456', '男')
# 打印所有属性
print(dir(user_info_2)) # ['_UserInfo__account', ...'lv', 'name', 'sex']
# 打印构造函数中的属性
print(user_info_2.__dict__) # {'name': 'Tom', '_age': 18, '_UserInfo__account': '123456', 'sex': '男'}
print(user_info_2.get_name()) # 5

7.4 子类的类型判断(isinstance)

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 User1(object):
pass


class User2(User1):
pass


class User3(User2):
pass


if __name__ == '__main__':
user1 = User1()
user2 = User2()
user3 = User3()
# isinstance()就可以告诉我们,一个对象是否是某种类型
print(isinstance(user3, User2)) # True
print(isinstance(user3, User1)) # True
print(isinstance(user3, User3)) # True
# 基本类型也可以用isinstance()判断
print(isinstance('两点水', str)) # True
print(isinstance(347073565, int)) # True
print(isinstance(347073565, str)) # False

8. 类的多态

多态是面向对象编程中一个重要的概念,它允许我们定义一个方法,该方法可以接受多个不同的类型参数,从而实现不同的功能。

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
class User(object):
def __init__(self, name):
self.name = name

def printUser(self):
print('Hello !' + self.name)


class UserVip(User):
def printUser(self):
print('Hello ! 尊敬的Vip用户:' + self.name)


class UserGeneral(User):
def printUser(self):
print('Hello ! 尊敬的用户:' + self.name)


def printUserInfo(user):
user.printUser()


if __name__ == '__main__':
userVip = UserVip('两点水')
printUserInfo(userVip) # Hello ! 尊敬的Vip用户:两点水
userGeneral = UserGeneral('水水水')
printUserInfo(userGeneral) # Hello ! 尊敬的用户:水水水

9. 类的访问控制

9.1 类属性的访问控制

Python 语言中没有真正的私有属性,用_开头只是语法规范,并非无法访问

  • __: 双下划线开头的属性是私有属性, 只能被类内部访问
  • _: 单下划线开头的属性是受保护的属性, 只能被类内部和子类访问
  • 不以下划线(_)开头的属性都是公共属性, 可以被类内部, 子类和外部访问
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class UserInfo:
lv = 5

def __init__(self, name, age, account):
self.name = name
self._age = age # 受保护的属性
self.__account = account # 私有属性

def get_account(self):
return self.__account

class UserInfo2(UserInfo):
pass

if __name__ == '__main__':
user_info_2 = UserInfo2('Tom', 18, '123456')
print(user_info_2.get_account()) # 123456

9.2 方法的访问控制

具体规则和类属性的访问控制一样,只是语法不同

1
2
3
4
5
6
7
8
9
class User(object):
def upgrade(self):
pass

def _buy_equipment(self): # 受保护的方法
pass

def __pk(self): # 私有方法
pass

9.3 类专有的方法

方法说明
__init__构造函数,在生成对象时调用
__del__析构函数,释放对象时使用
__repr__打印,转换
__setitem__按照索引赋值
__getitem__按照索引获取值
__len__获得长度
__cmp__比较运算
__call__函数调用
__add__加运算
__sub__减运算
__mul__乘运算
__div__除运算
__mod__求余运算
__pow__乘方

当然有些时候我们需要获取类的相关信息,我们可以使用如下的方法:

  • type(obj):来获取对象的相应类型;
  • isinstance(obj, type):判断对象是否为指定的 type 类型的实例;
  • hasattr(obj, attr):判断对象是否具有指定属性/方法;
  • getattr(obj, attr[, default]) 获取属性/方法的值, 要是没有对应的属性则返回 default 值(前提是设置了 default),否则会抛出 AttributeError 异常;
  • setattr(obj, attr, value):设定该属性/方法的值,类似于 obj.attr=value;
  • dir(obj):可以获取相应对象的所有属性和方法名的列表: