封装

把很多数据封装到一个对象中. 把固定功能的代码封装到一个代码块, 函数, 对象, 打包成模块. 这都属于封装的思想. 具体的情况具体分析. 比如. 你写了一个很牛B的函数. 那这个也可以被称为封装. 在面向对象思想中. 是把一些看似无关紧要的内容组合到一起统一进行存储和使用. 这就是封装.

封装的总共有两个特点:

  • 将内容封装到某个地方,以后再去调用被封装在某处的内容。
  • 一些私有的不想被外部访问的变量,也可以封装起来

我们首先来看第一个特性:

  • 将内容封装到某处
  • 从某处调用被封装的内容

第一步:将内容封装到某处
J53Q9s.jpg

self 是一个形式参数,当执行 obj1 = Foo(‘szk’, 18 ) 时,self 等于 obj1
当执行 obj2 = Foo(‘echo’, 78 ) 时,self 等于 obj2

所以,内容其实被封装到了对象 obj1 和 obj2 中,每个对象中都有 name 和 age 属性。

第二步:从某处调用被封装的内容

调用被封装的内容时,有两种情况:

  • 通过对象直接调用
  • 通过self间接调用

1、通过对象直接调用被封装的内容

上图展示了对象 obj1 和 obj2 在内存中保存的方式,根据保存格式可以如此调用被封装的内容:对象.属性名

1
2
3
4
5
6
7
8
9
10
11
class Foo:
def __init__(self, name, age):
self.name = name
self.age = age

obj1 = Foo('szk', 18)
print(obj1.name) # 直接调用obj1对象的name属性
print(obj1.age) # 直接调用obj1对象的age属性
obj2 = Foo('echo', 73)
print(obj2.name) # 直接调用obj2对象的name属性
print(obj2.age) # 直接调用obj2对象的age属性

2、通过self间接调用被封装的内容

执行类中的方法时,需要通过self间接调用被封装的内容

1
2
3
4
5
6
7
8
9
10
11
class Foo:
def __init__(self, name, age):
self.name = name
self.age = age
def detail(self):
print(self.name)
print(self.age)
obj1 = Foo('szk', 18)
obj1.detail() # Python默认会将obj1传给self参数,即:obj1.detail(obj1),所以,此时方法内部的 self = obj1,即:self.name 是 szk ;self.age 是 18
obj2 = Foo('echo', 73)
obj2.detail() # Python默认会将obj2传给self参数,即:obj1.detail(obj2),所以,此时方法内部的 self = obj2,即:self.name 是 echo ; self.age 是 78

接下来,我们再来看第二个特性:一些私有的不想被外部访问的属性和方法使用封装

1
2
3
4
5

class Human():
__salary = 1000000
def __sexy(self):
pass

继承

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
class Person:
def __init__(self,name,sex,age):
self.name = name
self.age = age
self.sex = sex
class Cat:
def __init__(self,name,sex,age):
self.name = name
self.age = age
self.sex = sex
class Dog:
def __init__(self,name,sex,age):
self.name = name
self.age = age
self.sex = sex

# 继承的用法:
class Aniaml(object):
def __init__(self,name,sex,age):
self.name = name
self.age = age
self.sex = sex
class Person(Aniaml):
pass
class Cat(Aniaml):
pass
class Dog(Aniaml):
pass




print(Person.__bases__) # 查看当前类的所有的基类
print(Cat.__bases__)

# 那你是否会好奇想看看我们定义的Parent类它有没有偷偷摸摸的继承谁呢?
print(Aniaml.__bases__)
print(Animal.__bases__)

# 切换python解释器3.x >>> 2.x得出一个结论

"""
在python3中,如果没有显示地继承任何类,那默认继承object类
在python2中,如果没有显示地继承任何类,也不会继承object类


在python中类分为两种:
新式类:
但凡继承object的类,或者该类的子类都是新式类
>>>:在python3中所有的类都是新式类
经典类
没有继承object类,以及该类的子类都是经典类

也就意味着经典类和新式类这对儿概念只在python2中有
python3中只有新式类
"""

继承的优点也是显而易见的:

  • 减少了类的耦合性(耦合性不宜多,宜精)。
  • 减少了重复代码。
  • 使得代码更加规范化,合理化。

继承的分类

分单继承,多继承。
python3中使用的都是新式类. 如果基类谁都不继承. 那这个类会默认继承 object

单继承
类名,对象执行父类方法
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 Aniaml(object):
type_name = '动物类'
def __init__(self,name,sex,age):
self.name = name
self.age = age
self.sex = sex
def eat(self):
print(self)
print('吃东西')

class Person(Aniaml):
pass
class Cat(Aniaml):
pass
class Dog(Aniaml):
pass

# 类名:
print(Person.type_name) # 可以调用父类的属性,方法。
Person.eat(111)
print(Person.type_name)

# 对象:
# 实例化对象
p1 = Person('春哥','男',18)
print(p1.__dict__)

# 对象执行类的父类的属性,方法。
print(p1.type_name)
p1.type_name = '666'
print(p1)
p1.eat()

执行顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Aniaml(object):
type_name = '动物类'
def __init__(self,name,sex,age):
self.name = name
self.age = age
self.sex = sex
def eat(self):
print(self)
print('吃东西')
class Person(Aniaml):
def eat(self):
print('%s 吃饭'%self.name)
class Cat(Aniaml):
pass
class Dog(Aniaml):
pass
p1 = Person('barry','男',18)
# 实例化对象时必须执行__init__方法,类中没有,从父类找,父类没有,从object类中找。
p1.eat()

# 先要执行自己类中的eat方法,自己类没有才能执行父类中的方法。

同时执行类以及父类方法

方法一:

如果想执行父类的func方法,这个方法并且子类中夜用,那么就在子类的方法中写上:

父类.func(对象,其他参数)

举例说明:

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 Aniaml(object):
type_name = '动物类'
def __init__(self,name,sex,age):
self.name = name
self.age = age
self.sex = sex
def eat(self):
print('吃东西')

class Person(Aniaml):
def __init__(self,name,sex,age,mind):
'''
self = p1
name = '春哥'
sex = 'laddboy'
age = 18
mind = '有思想'
'''
# Aniaml.__init__(self,name,sex,age) # 方法一
self.mind = mind

def eat(self):
super().eat()
print('%s 吃饭'%self.name)

class Cat(Aniaml):
pass
class Dog(Aniaml):
pass

# 方法一: Aniaml.__init__(self,name,sex,age)
# p1 = Person('春哥','laddboy',18,'有思想')
# print(p1.__dict__)

方法二:

利用super,super().func(参数)

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
class Aniaml(object):
type_name = '动物类'
def __init__(self,name,sex,age):
self.name = name
self.age = age
self.sex = sex
def eat(self):
print('吃东西')

class Person(Aniaml):
def __init__(self,name,sex,age,mind):
'''
self = p1
name = '春哥'
sex = 'laddboy'
age = 18
mind = '有思想'
'''
# super(Person,self).__init__(name,sex,age) # 方法二
super().__init__(name,sex,age) # 方法二
self.mind = mind
def eat(self):
super().eat()
print('%s 吃饭'%self.name)

class Cat(Aniaml):
pass
class Dog(Aniaml):
pass
# p1 = Person('春哥','laddboy',18,'有思想')
# print(p1.__dict__)

多继承

1
2
3
4
5
6
7
8
9
10
11
class ShenXian: # 神仙
def fei(self):
print("神仙都会飞")
class Monkey: # 猴
def chitao(self):
print("猴子喜欢吃桃子")
class SunWukong(ShenXian, Monkey): # 孙悟空是神仙, 同时也是一只猴
pass
sxz = SunWukong() # 孙悟空
sxz.chitao() # 会吃桃子
sxz.fei() # 会飞

此时, 孙悟空是一只猴子, 同时也是一个神仙. 那孙悟空继承了这两个类.
孙悟空自然就可以执行这两个类中的方法. 多继承用起来简单. 也很好理解. 但是多继承中, 存在着这样一个问题.
当两个父类中出现了重名方法的时候. 这时该怎么办呢? 这时就涉及到如何查找父类方法的这么一个问题.
即MRO(method resolution order) 问题. 在python中这是一个很复杂的问题.
因为在不同的python版本中使用的是不同的算法来完成MRO的.

经典类的多继承

经典类多继承的调用方法是顺序是:深度优先查询,如下图:

wpAVm9.png

测试一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class A:#经典类
def __init__(self):
self.n = "A"

class B(A):
def __init__(self):
self.n = "B"

class C(A):
def __init__(self):
self.n = "C"

class D(B,C):
def __init__(self):
self.n = "D"

d = D()
print d.n
新式类的多继承

新式类的继承使用的是广度优先

wpAVm9.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class A(object):  #新式类
def __init__(self):
self.n = "A"

class B(A):
def __init__(self):
self.n = "B"

class C(A):
def __init__(self):
self.n = "C"

class D(B,C):
def __init__(self):
self.n = "D"

d = D()
print(d.n)

mro序列

MRO是一个有序列表L,在类被创建时就计算出来。
通用计算公式为:
mro(Child(Base1,Base2)) = [ Child ] + merge( mro(Base1), mro(Base2), [ Base1, Base2] )
(其中Child继承自Base1, Base2)

上面就是C3算法,它是把我们多个类产生的共同继承留到最后去找. 所以. 我们也可以从图上来看到相关的规律. 这个要大家自己多写多画图就能感觉到了.

新式类:广度优先查询,经典类:深度优先查询(因为新式类讲究的是新,所以要找最近的,最新的;然后经典的讲究古老,所以更远更深的)

多态与多态性

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
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
class Animal:
def talk(self):
pass

class People(Animal):
def talk(self):
print('say hello')

class Dog(Animal):
def talk(self):
print('汪汪汪')

class Pig(Animal):
def talk(self):
print('哼哼哼')

peo1=People()
dog1=Dog()
pig1=Pig()

# 不用考虑对象具体类型的前提下,直接调用相同的方法
peo1.talk()
dog1.talk()
pig1.talk()

"""
再来想车是不是有很多牌子,你去学车需要说专门学哪个牌子的车的驾驶方式吗?
"""

# 来你之前也一直在用多态性:不用考虑对象具体类型的前提下,直接调用相同的方法
l=list([1,2,3])
s=str('hello')
t=tuple((4,5,6))

l.__len__()
s.__len__()
t.__len__() # 我不需要考虑这三个具体是什么类型,只要是容器类型就都能调用len这个方法

# 再来看多态性能够实现的条件是什么?父类有的方法,子类也必须有这个方法,并且名字也必须是一样的才行
class Animal:
def talk(self):
pass

class People(Animal):
def jiao(self):
print('say hello')

class Dog(Animal):
def han(self):
print('汪汪汪')

class Pig(Animal):
def hou(self):
print('哼哼哼')

# 多态性能实现的条件就是父类给子类定义了一个标准,动物都必须会叫,并且叫的方法都必须是talk
# 但是你现在能约束我说子类必须叫这个方法吗?

# 那有没有一种情况能够做到说子类必须按照父类定义的标准
import abc
class Animal(metaclass=abc.ABCMeta): # 父类存在的意义就是用来定义规范
@abc.abstractmethod
def talk(self):
pass

# Animal() # 抽象基类不能被实例化!!!
class People(Animal):
def jiao(self):
print('say hello')
class Dog(Animal):
def han(self):
print('汪汪汪')
class Pig(Animal):
def hou(self):
print('哼哼哼')
# 上面三个类 一实例化都会报错

# 但是python推崇的是自由,简约并不希望限制程序员的开发
# 鸭子类型:只要你长得像鸭子,说话像鸭子,那你就是鸭子!
class People:
def talk(self):
print('say hello')
class Dog:
def talk(self):
print('汪汪汪')
class Pig:
def talk(self):
print('哼哼哼')

# 再来看linux中:一切皆文件!
class Disk:
def read(self):
print('disk read')
def write(self):
print('disk write')
class Process:
def read(self):
print('process read')
def write(self):
print('processes write')
class Memory:
def read(self):
print('memory read')
def write(self):
print('memory write')

类中的装饰器

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

# property
# 人体的BMI指数
"""
成人的BMI数值:
过轻:低于18.5
正常:18.5-23.9
过重:24-27
肥胖:28-32
非常肥胖, 高于32
  体质指数(BMI)=体重(kg)/ 身高^2(m)
  EX:70kg÷(1.75×1.75)=22.86
"""
class People:
def __init__(self,name,height,weight):
self.name=name
self.height=height
self.weight=weight

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


p1 = People('szk',1.80,75)
p1.height=1.82
# print(szk.bmi())

print(szk.bmi)



# 了解
class People:
def __init__(self,name):
self.__name=name

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

@name.setter
def name(self,val):
# print('=====>准备修改名字的值:',val)
if type(val) is not str:
raise TypeError('名字的值必须为str类型')
self.__name=val

@name.deleter
def name(self):
# del self.__name
print('不让删啊老铁')

# classmethod

# 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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#1、反射:指的是通过字符串来操作类或者对象的属性
class People:
country='China'
def __init__(self,name):
self.name=name


obj = People('szk')


#涉及四个内置函数

# hasattr
print('country' in People.__dict__)
print(hasattr(People,'country'))

# getattr
print(People.__dict__['country'])
print(getattr(People,'country'))
print(getattr(People,'country1111',None))

# setattr
People.__dict__['x']=111
print(People.x)
setattr(People,'x',111)
print(People.__dict__)

# delattr
delattr(People,'country')
print(People.__dict__)


# 应用
class Ftp:
def get(self):
print('get...')

def put(self):
print('put...')

def auth(self):
print('auth...')

def run(self):
while True:
cmd=input('>>: ').strip() #cmd='get'
if hasattr(self,cmd):
method=getattr(self,cmd)
method()
else:
print('输入的方法不存在')
obj=Ftp()
obj.run()
总结

子类可以自动拥有父类中除了私有属性外的其他所有内容. 说白了, 儿子可以随便用爹的东西. 但是朋友们, 一定要认清楚一个事情. 必须先有爹, 后有儿子. 顺序不能乱, 在python中实现继承非常简单. 在声明类的时候, 在类名后面添加一个小括号,就可以完成继承关系. 那么什么情况可以使用继承呢? 单纯的从代码层面上来看. 两个类具有相同的功能或者特征的时候. 可以采用继承的形式. 提取一个父类, 这个父类中编写着两个类相同的部分. 然后两个类分别取继承这个类就可以了. 这样写的好处是我们可以避免写很多重复的功能和代码. 比如. 猫是一种动物. 猫继承动物. 动物能动. 猫也能动. 这时猫在创建的时候就有了动物的”动”这个属性. 再比如, 白骨精是一个妖怪. 妖怪天生就有一个比较不好的功能叫”吃人”, 白骨精一出生就知道如何”吃人”. 此时 白骨精继承妖精.