面向对象编程介绍

面向过程编程

核心是“过程”二字,过程指的是做事情的步骤,即先做什么再做什么。

基于该编程思想写程序,就好比一条工厂流水线,一种机械式的思维方式。

优点:逻辑清晰,复杂的问题流程化,进而简单化。

缺点:可扩展性差。

面向对象编程

面向对象编程的核心是对象二字,对象指得是特征与技能的结合体。基于该编程思想写程序,就好比在创造世界,一种上帝式的思维方式。

优点:可扩展性高。

缺点:编写程序的复杂程度远高于面向过程。

类和对象

什么是类?类型,分类

先定义类,后调用类产生对象。

  • 在现实世界中:对象是一个个具体存在的事物,类是由人类文明的发展抽象总结出来的。
  • 在程序中:必须遵循先有类,再有对象

总结:

对象是特征与技能的结合体,类是一系列对象相同的特征与技能的结合体。

定义类

1
2
3
4
5
6
7
8
# 类名使用驼峰命名法
class Student
school = 'oldboy'
def choose_course(self):
print('choose course')

def learn(self):
print('learn')

在定义类发生的事情:

  1. 类在定义时,会产生一个空的名称空间
  2. 会把类中定义的名字,扔进类的名称空间中

注意:类在定义阶段就已经产生好了名称空间,执行python文件时会执行类内部的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Student
school = 'oldboy'
def choose_course(self):
print('choose course')

def learn(self):
print('learn')


print(Student.__dict__) # 查看类的名称空间中所有名字

'''
{'__module__': '__main__', 'school': 'oldboy', 'choose_course': <function Student.choose_course at 0x0000010EF2FD3510>, 'learn': <function Student.learn at 0x0000010EFA1A0950>, '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None}
'''

对象的创建和使用

类的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Student
school = 'oldboy'
def choose_course(self): # self就是对象本身
print('choose course')

def learn(self):
print('learn')

print(Student.school) # 查找

Student.school = 'oldgirl' # 修改
print(Student.school)
'''
oldgirl
'''

Student.address = '上海' # 增加
print(Student.address)

del Student.address # 删除


Student.choose_course(123) # 括号里面需要参数

名称空间的产生:

  1. 类的名称空间在定义阶段就已经产生了
  2. 对象的名称空间,在调用类时产生
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Student():
school = 'oldboy'

def choose_course(self):
print('choose course')

def learn(self):
print('learn')


stu1 = Student()
stu2 = Student()
stu3 = Student()

print(stu1.school)
stu1.choose_course()
stu1.learn()

在类内部定义__init__函数

__init__会在调用类时自动触发该函数,为对象初始化某些属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Student():
school = 'oldboy'

def __init__(self):
print('此处是__init__')


def choose_course(self):
print('choose course')

def learn(self):
print('learn')


stu1 = Student()
stu2 = Student()
stu3 = Student()

print(stu1.school)
stu1.choose_course()
stu1.learn()

调用类发生的事情:

  1. 首先会产生一个空的对象,就是产生“对象的名称空间”
  2. 会自动触发__init__
  3. 会把对象以及括号内的参数一并传给__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
25
26
27
class Student():
school = 'oldboy'

def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex


def choose_course(self):
print('choose course')

def learn(self):
print('learn')


stu1 = Student('cwz', 23, 'male')

print(stu1.name)
print(stu1.age)
print(stu1.sex)

'''
cwz
23
male
'''

对象与类的查找顺序

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 Student():
school = 'aaa'

def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex


def choose_course(self):
print('choose course')

def learn(self):
print('learn')


stu1 = Student('cwz', 23, 'male')

print(stu1.__dict__)
print(stu1.name, stu1.school)
'''
{'name': 'cwz', 'age': 23, 'sex': 'male'}
cwz aaa
'''

对象与类的查找顺序:

  1. 对象.属性,若对象本身有,则优先查找对象自己的
  2. 若对象本身没有,则去类里面找,若类里面没有,则报错

对象绑定方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Student():
school = 'oldboy'

def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
print(self)


def choose_course(self):
print('choose course')

def learn(self):
print('learn')

# print(Student.learn)
# Student.learn(133)

stu1 = Student('qwe', 20, 'male')
stu1.learn()

类内部的函数主要是给对象用的:

  1. 由类来调用类内部的函数,该函数只是一个普通的函数,普通函数需要接收几个参数就得传入几个参数
  2. 由对象来调用称之为对象的绑定方法,不同的对象调用该绑定方法,则会将不同的对象传入该绑定方法中
  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
34
35
36
class Student():
school = 'oldboy'

def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
print('stu:', self)


def choose_course(self):
print('choose course')

def learn(self):
print('learn')

# print(Student.learn)
# Student.learn(133)

stu1 = Student('qwe', 20, 'male')
stu2 = Student('cwz', 21, 'male')
stu1.learn()
print('stu1', stu1)
stu2.learn()
print('stu2', stu2)
print(stu1.learn)

'''
stu: <__main__.Student object at 0x0000019E249E2128>
stu: <__main__.Student object at 0x0000019E249E2198>
learn
stu1 <__main__.Student object at 0x0000019E249E2128>
learn
stu2 <__main__.Student object at 0x0000019E249E2198>
<bound method Student.learn of <__main__.Student object at 0x0000019E249E2128>>
'''

小练习

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
'''
人狗互咬大战
'''

class Person:
def __init__(self, name, aggr, life):
self.name = name
self.aggr = aggr
self.life = life

# 人咬狗方法
def bite(self, dog):
if dog.life < 0:
return True
dog.life -= self.aggr

print(
f'''
{self.name}咬狗 {dog.name}
狗掉血 {self.aggr}
狗还剩的血量 {dog.life}
'''
)


class Dog:
def __init__(self, name, dog_type, aggr, life):
self.name = name
self.dog_type = dog_type
self.aggr = aggr
self.life = life

# 狗咬人方法
def bite(self, person):
if person.life < 0:
return True
person.life -= self.aggr

print(
f'''
{self.name}咬人 {person.name}
人掉血 {self.aggr}
人还剩的血量 {person.life}
'''
)


p1 = Person('neo', 300, 400)
d1 = Dog('大黄', '土狗', 200, 500)

while True:
flag1 = p1.bite(d1)
if flag1:
break

flag2 = d1.bite(p1)
if flag2:
break

继承

什么是继承

继承指的是新建类的方法,新建的类称之为子类或派生类;

子类继承的类为父类,也称之为基类或超类。

继承的特征

子类可以继承父类的属性(特征与技能),并且可以派生出自己的属性(特征与技能)

为什么要继承

继承的目的是为了减少代码的冗余(减少代码量)

如何实现继承

  1. 首先要确定谁是父类,谁是子类
  2. 在定义类时,子类+(),()内写父类,实现继承
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class :
pass

class ():
pass
class Parent1:
pass

class Parent2:
pass


class Sub1(Parent1):
pass


class Sub2(Parent1, Parent2):
pass

# 查看继承的父类: __bases__, 用来查找当前类的父类
print(Sub1.__bases__) # (<class '__main__.Parent1'>,)

print(Sub2.__bases__) # (<class '__main__.Parent1'>, <class '__main__.Parent2'>)

注:一个子类可以继承多个父类,这是python独有的,其他语言只能一个子类继承一个父类

继承与抽象

如何寻找继承关系

想要寻找继承关系,首先要先抽象,再继承

抽象:抽象指的是抽取相似的地方

  • 先抽象(抽象思想)
    • 奥巴马 —-> 人类 —-> 动物类
    • 猪坚强 —-> 猪类 —-> 动物类
    • 阿黄 —-> 狗类 —-> 动物类
    • 抽象定义动物类,为父类
  • 再继承(程序中)
    • 奥巴马对象 —-> 调用人类 —-> 继承动物类
    • 猪坚强对象 —-> 调用猪类类 —-> 继承动物类
    • 阿黄对象 —-> 调用狗类 —-> 继承动物类

继承的关系

  • 对象是特征与技能的结合体
  • 类是一系列相同对象的特征与技能的结合体
  • 继承是一系列形同类的特征与技能的结合体
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 People:
school = 'oldboy'

def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex


class Teacher(People):
def change_score(self):
print(f'{self.name}老师正在该分数。。。')


class Student(People):
def choose_course(self):
print(f'{self.name}同学正在选课。。。')


tea1 = Teacher('tank', 18, 'male')
stu1 = Student('小明', 18, 'male')

tea1.change_score()
stu1.choose_course()

'''
tank老师正在该分数。。。
小明同学正在选课。。。
'''

对象属性的查找顺序

  1. 对象查找属性会先从对象的名称空间中查找。
  2. 若对象没有,则从类中查找
  3. 若当前类是子类,并且没有对象找的属性,就去父类中查找
  4. 若父类中也没有机会报错

注:对象查找属性,若子类有,不管父类有没有,以子类的为准

验证查找顺序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Foo:
def f1(self):
print('Foo.f1')



def f2(self):
print('Foo.f2')
self.f1()


class Soo(Foo):
def f1(self):
print('Soo.f1')


soo_obj = Soo()
soo_obj.f2()
'''
Foo.f2
Soo.f1
'''

首先soo_obj从对象自身找,没有去子类Soo中找,没找到,再去父类Foo中找,找到了打印’Foo.f2’,接着就是self.f1(),self就是对象本身,按照对象—-> 子类—->父类的顺序找,再子类Soo中找到了打印’Soo.f1’

派生

派生指的是子类继承父类的属性,并且派生出新的属性。

子类派生出新的属性,若与父类的属性相同,则以子类的为准。

继承是谁与数的关系,指的是类与类的关系,子类与父类是从属关系

派生方法一(类调用)

直接通过父类.__init__,把__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
25
26
27
28
29
class People:
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex

class Teacher(People):
def __init__(self, name, age, sex, level, salary):
People.__init__(self, name, age, sex)
self.level = level
self.salary = salary


class Student(People):
def __init__(self, name, age, sex, course):
People.__init__(self, name, age, sex)
self.course = course

def choose_course(self):
print(f'{self.name} is choosing {self.course}')


tea1 = Teacher('tank', 18, 'male', 9, '3.0')
stu1 = Student('小明', 18, 'male', 'python')
stu1.choose_course()

'''
小明 is choosing python
'''

派生方法二(super)

  • super是一个特殊的类,在子类中调用super()会得到一个特殊的对象,通过”.”指向父类的名称空间
  • super().__init__(不用为self传值)
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
class People:
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex

class Teacher(People):
def __init__(self, name, age, sex, level, salary):
super().__init__(name, age, sex)
self.level = level
self.salary = salary


class Student(People):
def __init__(self, name, age, sex, course):
super().__init__(name, age, sex)
self.course = course

def choose_course(self):
print(f'{self.name} is choosing {self.course}')


tea1 = Teacher('tank', 18, 'male', 9, '3.0')
stu1 = Student('小明', 18, 'male', 'python')
stu1.choose_course()

新式类与经典类

  • 在python2中,才会有新式类与经典类之分
  • 在python3中,所有的类都是新式类

新式类

  • 继承object的类都是新式类
  • python3中,子类不继承自定义的类,默认继承object

经典类

在python2中,凡是没有继承object的类都是经典类

mro查看继承顺序

mro(): 是python内置的函数,用来查看当前类的继承顺序,在多继承的情况下

1
2
3
4
5
6
7
8
9
10
11
12
class A:
pass

class B:
pass

class C(A, B):
pass

print(C.mro())

# [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]

钻石继承(菱形继承)

在多继承的情况下形成钻石继承

  • 经典类: 深度优先
  • 新式类: 广度优先

修改json数据格式

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
import json
from datetime import datetime
from datetime import date

dic = {
'name': 'tank',
'today1': date.today(),
'today2': datetime.today()
}

class MyJson(json.JSONEncoder):
def default(self, o):
# 判断o是否是datetime的一个实例
if isinstance(o, datetime): # instance 是python内置的函数,可以传两个参数,判断参数一是否是参数二的一个实例
return o.strftime('%Y-%m-%d %X')
elif isinstance(o, date):
return o.strftime('%Y-%m-%d %X')
else:
# 继承父类default方法的功能
return super().default(self, o)

res = json.dumps(dic, cls=MyJson) # cls=None, 默认指向的是原json的JSONEncoder
print(res)
'''
{"name": "tank", "today1": "2019-10-10 00:00:00", "today2": "2019-10-10 17:00:20"}
'''

组合

什么是组合

组合指的是一个对象中的属性,是另一个对象

为什么要使用组合

减少代码冗余

如何使用组合

继承实现:

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
# 选课系统:老师类,学生类,老师与学生都有名字、年龄、性别

class People:
def __init__(self, name, age, sex, year, month, day):
self.name = name
self.age = age
self.sex = sex
self.year = year
self.month = month
self.day = day

def tell_birth(self):
print(f'''
=== 出生年月日 ===
年: {self.year}
月: {self.month}
日: {self.day}
''')


class Teacher(People):
def __init__(self, name, age, sex, year, month, day):
super().__init__(name, age, sex, year, month, day)


class Student(People):
def __init__(self, name, age, sex, year, month, day):
super().__init__(name, age, sex, year, month, day)


tea1 = Teacher('tank', 18, 'male', 2001, 1, 1)
tea1.tell_birth()

组合实现:

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 People:
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex


class Teacher(People):
def __init__(self, name, age, sex):
super().__init__(name, age, sex)

class Student(People):
def __init__(self, name, age, sex):
super().__init__(name, age, sex)


class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day

def tell_birth(self):
print(f'''
=== 出生年月日 ===
年: {self.year}
月: {self.month}
日: {self.day}
''')


tea1 = Teacher('tank', 18, 'male')
date_obj = Date(2001, 1, 1)
# 将date对象赋值给tea1对象的属性date中
tea1.date = date_obj
tea1.date.tell_birth()

总结:

  • 继承是类与类的关系,一种什么是什么的关系,子类与父类是一种从属关系
  • 组合是对象与对象的关系,一种什么有什么的关系,一个对象拥有另一个对象

组合练习

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
'''
选课系统需求:
1.学生类,老师类, 学生和老师都有课程属性, 每一门课程都是一个对象.
课程: 课程名字,课程周期,课程价钱

2.学生和老师都有选择课程的功能, 还有打印所有课程的功能.
'''


class People:
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex

def add_course(self, course_obj):
self.course_list.append(course_obj)

def tell_all_course(self):
# 拿到当前对象的课程列表,列表中存放的是一个个对象
for course_obj in self.course_list:
# 每一个课程对象查看课程信息的方法
course_obj.tell_course_info()


class Teacher(People):
def __init__(self, name, age, sex):
super().__init__(name, age, sex)
self.course_list = []


class Student(People):
def __init__(self, name, age, sex):
super().__init__(name, age, sex)
self.course_list = []


class Course:
def __init__(self, course_name, course_period, course_price):
self.course_name = course_name
self.course_period = course_period
self.course_price = course_price

def tell_course_info(self):
print(f'''
课程名称:{self.course_name}
课程周期:{self.course_period}
课程价格:{self.course_price}
''')

tea1 = Teacher('tank', 18, 'male')
stu1 = Student('小明', 18, 'male')

python_obj = Course('python', 6, 2)
linux_obj = Course('linux', 6, 1)

tea1.add_course(python_obj)
tea1.add_course(linux_obj)

tea1.tell_all_course()

封装

什么是封装

封装指的是把一堆属性封装到一个对象中

存数据的目的是为了取,对象可以”.”的方式,获取属性

为什么要封装

封装的目的是为了方便存取,可以通过对象.属性的方式获取属性

如何封装

特征:变量 —-> 数据属性

技能:函数 —-> 方法属性

在类内部,定义一堆属性(特征与技能)

访问限制机制

什么是访问限制机制

在类内部定义,凡是以__开头的数据属性与方法属性,都会被python内部隐藏起来,让外部不能直接访问内部的__开头的属性,如:__name = ‘小in’

访问限制机制的目的

一堆隐私的属性与不能被外部轻易访问的属性,可以隐藏起来,不能被外部直接调用

好处:对重要数据获取的逻辑更加严谨,进而保护了数据的安全

隐私属性可以通过封装一个接口,在接口内做业务逻辑的处理,再把数据返回给调用者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Foo:
# 数据属性
__name = 'tank'

# 方法属性
def __run(self):
print('running...')

def get_name(self):
return self.__name

def set_name(self):
self.__name = 'cwz'

foo = Foo()
# print(foo.__name)
foo.set_name()
print(foo.get_name())

print(foo._Foo__name) # _类名__属性名

注意:在python中,不会强制限制属性的访问,类内部__开头的属性,只是做了一种变形。若想直接访问,调用变形后的名字即可

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
class Teacher:
def __init__(self, name, age, sex):
self.__name = name
self.__age = age
self.__sex = sex

# 接口:打印用户信息接口
def get_info(self):
user = input('user:').strip()
pwd = input('pwd:').strip()
if user == 'tank' and pwd == '123':

print(f'''
姓名:{self.__name}
年龄:{self.__age}
性别:{self.__sex}
''')

# 接口:修改用户信息接口
def set_info(self, name, age, sex):
if not isinstance(name, str):
raise TypeError('名字必须要使用字符串')

if not isinstance(age, int):
raise TypeError('年龄必须要使用数字')

if not isinstance(sex, str):
raise TypeError('性别必须要使用字符串')

self.__name = name
self.__age = age
self.__sex = sex

t1 = Teacher('tank', 18, 'male')
t1.get_info()
class ATM:
# 插卡
def __insert_card(self):
print('开始插卡')

# 输入密码
def __input_pwd(self):
print('开始输入密码')

# 输入取款金额
def __input_money(self):
print('输入取款金额')

# 开始吐钱
def __get_money(self):
print('开始吐钱')

# 打印账单
def __print_flow(self):
print('打印账单')

# 取钱接口
def withdraw(self):
self.__insert_card()
self.__input_pwd()
self.__input_money()
self.__get_money()
self.__print_flow()
print('程序执行完毕')

atm = ATM()
atm.withdraw()

property

什么是property

python内置的装饰器,主要是给类内部的方法使用

为什么要用property

使用它的目的,是将类内部的方法(def 方法名() 变成了(def 方法))

在对象使用某个方法时,将对象.方法()变成了对象.方法

如何使用property

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
'''
计算人的bmi:bmi值 = 体重 / (身高 * 身高)
'''

class People:
def __init__(self, name, weight, height):
self.name = name
self.weight = weight
self.height = height

@property
def bmi(self):
return self.weight / (self.height ** 2)

p = People('cwz', 180, 1.8)
print(p.bmi)

注意:不能对被装饰过的方法属性修改

但是也可以修改(了解)

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
class People:
def __init__(self, name, weight, height):
self.name = name
self.weight = weight
self.height = height

@property
def bmi(self):
return self.weight / (self.height ** 2)

@property
def get_name(self):
return self.name

# 修改
@get_name.setter
def set_name(self, val):
self.name = val

# 删除
@get_name.deleter
def del_name(self):
del self.name


p = People('cwz', 180, 1.8)
# print(p.bmi)

p.set_name = 'nick'
# print(p.get_name)

del p.del_name

# print(p.get_name)

多态

什么是多态

多态指的是同一种事物的多种形态

多态的目的

多态也称之为多态性,在程序中继承就是多态的表现形式

多态的目的是为了,让多种不同类型的对象,在使用相同功能的情况下,调用同一个名字的方法名

父类:定义一套统一的标准

子类:遵循父类统一的标准

多态的最终目的:统一子类编写的规范,为了让使用者更方便调用相同方法的功能

如何实现:

利用继承

抽象类

在python中,不会强制要求子类必须遵循父类的一套标准,所以出现了抽象类

abc模块

会强制子类遵循父类的一套标准

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
import abc


class Animal(metaclass=abc.ABCMeta):
@abc.abstractmethod
def eat(self):
pass

@abc.abstractmethod
def drink(self):
pass

@abc.abstractmethod
def speak(self):
pass


class Pig(Animal):
def eat(self):
pass

def drink(self):
pass

def jiao(self): # 强制使用与父类相同的标准,这样会报错
print('哼哼哼。。。')


pig = Pig()
pig.jiao()

鸭子类型

在不知道当前对象是什么的情况下,但你长得像鸭子,那么你就是鸭子类型

在python中,不推荐使用抽象类强制子类的定义,但是推荐子类都遵循鸭子类型

  • 继承:耦合性太高,程序的可扩展性差
  • 鸭子类型:耦合度第,程序的可扩展性强

多态炫技操作

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
class Animal:
def eat(self):
pass

def drink(self):
pass

def speak(self):
pass


class Pig(Animal):
def eat(self):
pass

def drink(self):
pass

def speak(self):
print('哼哼哼。。。')


class Cat(Animal):
def eat(self):
pass

def drink(self):
pass

def speak(self):
print('喵喵喵。。。')


class Dog(Animal):
def eat(self):
pass

def drink(self):
pass

def speak(self):
print('汪汪汪。。。')


pig = Pig()
dog = Dog()
cat = Cat()


def BARK(animal):
animal.speak()


BARK(dog)
BARK(pig)
BARK(cat)

类的绑定方法和非绑定方法

classmethod

classmethod是一个装饰器,可以装饰给类内部的方法,使该方法绑定给类来使用

  1. 对象绑定方法特殊之处:由对象来调用,会把对象当作第一个参数传给该方法
  2. 类绑定方法特殊之处:由类来调用,会把类当作第一个参数传给该方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class People:
def __init__(self, name, age):
self.name = name
self.age = age

@classmethod
def tell_info(cls):
print(cls)
print('此处是类方法。。。')


People.tell_info()
'''
<class '__main__.People'>
此处是类方法。。。
'''

小练习:

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
# settings.py
'''
USER = 'tank'
PWD = '123'
'''


import settings

class Teacher:
def __init__(self, user, pwd):
self.user = user
self.pwd = pwd

# 显示主页
def index(self):
if self.user == 'tank' and self.pwd == '123':
print('验证成功,显示主页')

@classmethod
def login_auth(cls):
obj = cls(settings.USER, settings.PWD)
return obj


obj = Teacher.login_auth()
obj.index()

staticmethod

staticmethod是一个装饰器,可以装饰给类内部的方法,使得该方法既不绑定类,也不绑定对象

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
import settings
import uuid
import hashlib

class Teacher:
def __init__(self, user, pwd):
self.user = user
self.pwd = pwd

# 显示主页
def index(self):
if self.user == 'tank' and self.pwd == '123':
print('验证成功,显示主页')

@classmethod
def login_auth(cls):
obj = cls(settings.USER, settings.PWD)
return obj

@staticmethod
def create_id():
uuid_obj = uuid.uuid4()
md5 = hashlib.md5()
md5.update(str(uuid_obj).encode('utf-8'))
return md5.hexdigest()


print(Teacher.create_id())
t1 = Teacher('tank', 123)
print(t1.create_id())

isinstance和issubclass

instance

python内置函数,可以传入两个参数,用于判断参数1是否是参数2的一个实例。

判断一个对象是否是一个类的实例

1
2
3
4
5
6
7
8
9
class Foo:
pass

foo_obj = Foo()
print(foo_obj)
print(foo_obj.__class__.__dict__)
print(foo_obj in Foo.__dict__) # 判断一个对象是否是类的实例

print(isinstance(foo_obj, Foo)) # 判断一个对象是否是类的实例

issubclass

python内置函数,可以传入两个参数,用于判断参数1是否是参数2的一个子类

判断一个类是否是一个类的子类

1
2
3
4
5
6
7
8
9
class Foo:
pass


class Goo(Foo):
pass


print(issubclass(Goo, Foo)) # True

反射

指的是通过“字符串”对 对象或类的属性进行操作

hasatter

通过字符串,判断字符串是否是对象或类的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class People:
country = 'China'

def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex


p = People('tank', 18, 'male')
# 普通方式
print('name' in p.__dict__)
# hasatter
print(hasattr(p, 'name'))
print(hasattr(p, 'country'))

getatter

通过字符串,获取对象或类的属性4

1
2
3
4
# 普通方式
print(p.__dict__.get('name'))
# getatter
print(getattr(p, 'name', 'jason'))

setatter

通过字符串,设置对象或类的属性

1
2
setattr(p, 'level', '3.0')
print(hasattr(p, 'level'))

delatter

通过字符串,删除对象或类的属性

1
2
3
delattr(p, 'name')

print(hasattr(p, 'name'))

反射小练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Movie:
def input_cmd(self):
print('请输入命令......')
while True:
cmd = input('请输入执行的命令:').strip()

# 若输入的cmd命令是当前对象的属性
if hasattr(self, cmd):
method = getattr(self, cmd)
method()


def upload(self):
print('正在上传...')

def download(self):
print('正在下载...')


obj = Movie()
obj.input_cmd()

魔法方法

凡是类内部定义,以__开头,__结尾的方法都称之为魔法方法,又称类的内置方法

魔法方法会在某些条件成立的时候触发

__init__:在掉用类时触发
__str__:在打印对象时触发

__del__:会在程序结束时触发,销毁对象, 该方法会在最后执行

__getattr__:会在对象.属性时,属性没有的情况下才会触发

__setattr__:会在对象.属性 = 属性值的时候触发

__new__:会在__init__执行前触发
__call__:会在对象调用时触发

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
37
38
39
40
41
42
class Foo:

def __new__(cls, *args, **kwargs):
print(cls)
return object.__new__(cls) # 真正产生一个对象

# 若当前类的__new__没有return一个空对象,则不会触发
def __init__(self):
print('在调用类时触发...')

def __str__(self):
print('在打印对象时触发')
# 必须要有一个返回值,该返回值必须是字符串
return '我是Foo实例化出的对象'

def __del__(self):
print('对象被销毁前执行该程序')

def __getattr__(self, item):
print('会在对象.属性时,属性没有的情况下才会触发')
print(item)
# 若想打印属性的结果,必须要return一个值

# 注意:执行该方法时,外部“对象.属性=属性值”时无效
def __setattr__(self, key, value):
print('会在对象.属性 = 属性值的时候触发')
print(key, value)
print(self, 111)
self.__dict__[key] = 1234

def __call__(self, *args, **kwargs):
print(self)
print('调用对象时触发')


obj = Foo()
# print(obj)
# print(obj.x)
# obj.x = 3
# print(obj.x)
#
# obj()

魔法方法del应用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class MyFile(object):
def __init__(self, file_name, mode='r', encoding='utf-8'):
self.file_name = file_name
self.mode = mode
self.encoding = encoding

def file_open(self):
self.f = open(self.file_name, self.mode, encoding=self.encoding)

def file_read(self):
res = self.f.read()
print(f'''
当前文件名称:{self.file_name}
当前文件内容:{res}
''')

def __del__(self):
self.f.close()
print('文件关闭成功')

f = MyFile('settings.py')
f.file_open()
f.file_read()

元类

创建类的两种方式

  1. 用关键字class创建类,就会自动调用type(),type会自动帮我创建一个类
1
2
3
4
5
6
7
8
9
10
11
class People:
def __init__(self, name, age):
self.name = name
self.age = age

def speak(self):
print('speaking....')

p = People('小明', 20)
print(type(p)) # <class '__main__.People'>
print(type(People)) # <class 'type'>
  1. 手动调用type(),实例化得到一个类

type() 括号里传三个参数:

  • 参数1 类名
  • 参数2 基类/父类
  • 参数3 类的名称空间
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class_name = 'People'
class_base = (object,)
class_dict = {}

code = '''
country = "China"
def __init__(self, name, age):
self.name = name
self.age = age
def speak(self):
print('speaking...')
'''
exec(code, {}, class_dict)

People = type(class_name, class_base, class_dict)
print(People) # <class '__main__.People'>

什么是元类

元类就是类的类,如上述例子中,class创建了一个People类,那People这个类是type的一个实例,type就是一个元类。

元类的作用

元类:就是控制类的创建

创建元类

  • 自定义一个元类,继承type,派生出自己的属性和方法
  • 给需要使用的类,通过metaclass指定自定义的元类

现在有两个需求:

  1. 要求创建类的类名必须首字母大写
  2. 要求创建类,必要在类中写注释
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
class MyMeta(type):
def __init__(self, class_name, class_base, class_dict):
super().__init__(class_name, class_base, class_dict)
print(class_name)

# 控制类名必须首字母大写
if not class_name.istitle():
raise TypeError('类名必须首字母大写')

# 控制 类必须写注释
if not class_dict.get('__doc__'):
raise TypeError('类中要写注释')

# 元类触发的__call__,会控制类的调用
def __call__(self, *args, **kwargs):
# 1.调用__new__, 创建一个空对象
obj = object.__new__(self)
# 2.执行__init__(obj, *args, **kwargs)
obj.__init__(*args, **kwargs)
return obj


class Bar:
pass


class Foo(Bar, metaclass=MyMeta):
'''这是一个Foo类'''
x = 10

def f1(self):
print('from f1')

f = Foo() # 调用Foo 对象, 会触发__call__

单例模式

什么是单例

单例模式指的是单个实例,实例指的是调用类产生的对象

为什么要使用单例

实例化多个对象会产生不同的内存地址,单例可以调用者在调用同一个对象时,都指向同一个内存地址。例如打开文件。

单例的目的:为了减少内存的占用。

实现单例模式的几种方式

使用模块

其实,Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码。因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。如果我们真的想要一个单例类,可以考虑这样做:

1
2
3
4
5
6
# mysingleton.py

class Singleton(object):
def foo(self):
pass
singleton = Singleton()

将上面的代码保存在文件 mysingleton.py 中,要使用时,直接在其他文件中导入此文件中的对象,这个对象即是单例模式的对象

1
from mysingleton import singleton

类内部定义静态方法

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
37
38
class File:

__instance = None

# 单例模式1
@classmethod
def singleton(cls, file_name):
if not cls.__instance:
obj = cls(file_name)
cls.__instance = obj
return cls.__instance

def __init__(self, file_name, mode='r', encoding='utf-8'):
self.file_name = file_name
self.mode = mode
self.encoding = encoding

def open(self):
self.f = open(self.file_name, self.mode, encoding=self.encoding)

def read(self):
res = self.f.read()
return res

def close(self):
self.f.close()

obj1 = File.singleton('settings.py')
obj2 = File.singleton('settings.py')
obj3 = File.singleton('settings.py')
print(obj1)
print(obj2)
print(obj3)
'''
<__main__.File object at 0x000001A04F472198>
<__main__.File object at 0x000001A04F472198>
<__main__.File object at 0x000001A04F472198>
'''

装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def Singleton(cls):
_instance = {}

def _singleton(*args, **kargs):
if cls not in _instance:
_instance[cls] = cls(*args, **kargs)
return _instance[cls]

return _singleton


@Singleton
class A(object):
a = 1

def __init__(self, x=0):
self.x = x


a1 = A(2)
a2 = A(3)

使用__new__

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 File:

__instance = None

# 单例模式2
def __new__(cls, *args, **kwargs):
if not cls.__instance:
cls.__instance = object.__new__(cls)
return cls.__instance

def __init__(self, file_name, mode='r', encoding='utf-8'):
self.file_name = file_name
self.mode = mode
self.encoding = encoding

def open(self):
self.f = open(self.file_name, self.mode, encoding=self.encoding)

def read(self):
res = self.f.read()
return res

def close(self):
self.f.close()

obj1 = File('settings.py')
obj2 = File('settings.py')
obj3 = File('settings.py')
print(obj1)
print(obj2)
print(obj3)
'''
<__main__.File object at 0x00000184C9CD2198>
<__main__.File object at 0x00000184C9CD2198>
<__main__.File object at 0x00000184C9CD2198>
'''

基于元类实现

1
2
3
4
"""
1.类由type创建,创建类时,type的__init__方法自动执行,类() 执行type的 __call__方法(类的__new__方法,类的__init__方法)
2.对象由类创建,创建对象时,类的__init__方法自动执行,对象()执行类的 __call__ 方法
"""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import threading

class SingletonType(type):
_instance_lock = threading.Lock()
def __call__(cls, *args, **kwargs):
if not hasattr(cls, "_instance"):
with SingletonType._instance_lock:
if not hasattr(cls, "_instance"):
cls._instance = super(SingletonType,cls).__call__(*args, **kwargs)
return cls._instance

class Foo(metaclass=SingletonType):
def __init__(self,name):
self.name = name


obj1 = Foo('name')
obj2 = Foo('name')
print(obj1,obj2)