6.1:类与对象
类、对象
实例:增删改查
6.2:类调用方式
普通调用方式:
静态属性
类方法
类的工具包
6.3: 三大特性: 继承,封闭,派生
继承顺序:mro
子类调用父类的方法
supper()
多态
封装
私有属性
6.4: 常用术语
合成
派生/继承/继承结构
泛化/特化
多态与多态性
自省/反射
python是一门面向对象编程语言,其中编程方式分为三种范式:
1、面向过程编程
2、函数式编程
分为两种:数学层次的编程与python函数式编程
3、面向对象编程
对象是由类产生的具体存在
6.1: 类与对象
什么是类:
类:把一类事物的相同的特征和动作整合到一起就是类,类是一个抽象的概念
什么是对象:
对象:就是基于类而创建的一个具体的事物(具体存在的),也是特征和动作整合到一起。
面向对象设计: 把一类事物的相同的数据和动作整合到一起,即是面向对象设计
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
def
Botany(name,variety,
type
):
# 特征,名称,品种,类型
def
init(name,variety,
type
):
Bot
=
{
'Name'
:name,
'variety'
:variety,
'type'
:
type
,
'see'
:see
}
return
Bot
# 动作,查看一下它
def
see(Botany):
print
(
'这是一朵 %s花'
%
Botany[
'Name'
])
return
init(name,variety,
type
)
fy
=
Botany(
'玫瑰'
,
'花'
,
'观赏类'
)
print
(fy)
fy[
'see'
](fy)
结果:
{
'Name'
:
'玫瑰'
,
'variety'
:
'花'
,
'type'
:
'观赏类'
,
'see'
: <function Botany.<
locals
>.see at
0x00000000027CF840
>}
这是一朵 玫瑰花
|
面向对象编程: 用定义类+实例/对象的方式去实现面向对象的设计
类:
1
2
3
4
5
|
class
类名: 经典类
pass
class
类名(
object
): 新式类
pass
|
# 在python3中,上述两种定义方式全都是新式类
class.__dict__ 查看类的属性字典
1
2
3
4
5
6
7
8
9
10
11
12
|
class
te1:
def
__init__(
self
,name):
self
.name
=
name
def
play_tv(
self
):
print
(
'%s 正在看电视'
%
self
.name)
def
in_name():
name
=
input
(
'请输入名称: '
)
# 类的每个功能体都必须放开,init也可以定义,init建议只存放字典类数据
p1
=
te1(name)
p1.play_tv()
in_name()
|
结果:
1
2
|
请输入名称: xx
xx 正在看电视
|
实例:指的是类生成的某个对象,调用 __init__ 的过程
实例应当具备类的特征以及数据属性
实例没有函数属性, 函数属性只属于类
实例只建议使用查,不建议修改或增加函数。
# 创建一个类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
class
te1:
# 创建构造方法
def
__init__(
self
,name,age,gender):
self
.name
=
name
self
.age
=
age
self
.gender
=
gender
huan
=
te1(
'huan'
,
111
,
'man'
)
# 查看类的字典
print
(huan.__dict__)
# {'name': 'huan', 'age': 111, 'gender': 'man'}
# 修改
huan.age
=
'222'
print
(huan.age)
# 222
# 增加一个键值对
huan.__dict__[
'hu'
]
=
'test'
print
(huan.__dict__)
# {'name': 'huan', 'age': '222', 'gender': 'man', 'hu': 'test'}
# 删除
del
huan.age
print
(huan.__dict__)
# {'name': 'huan', 'gender': 'man'}
|
# 说明
# self: 调用实例属性
# cls: 调用类属性
6.2:类调用方式
# 普通调用方式:
1
2
3
4
5
6
7
8
9
10
11
12
|
class
Cup:
def
__init__(
self
,name,size,
type
):
self
.Name
=
name
self
.Size
=
size
self
.
Type
=
type
def
info(
self
):
print
(
'the cup name is: %s size: %s type: %s'
%
(
self
.Name,
self
.Size,
self
.
Type
))
c1
=
Cup(
'mo'
,
'11'
,
'bf'
)
c1.info()
# 结果: the cup name is: mo size: 11 type: bf
|
静态属性: 将函数封闭成数据属性的格式,调用时可直接忽略执行时的逻辑
# 错误的调用方式
1
2
3
4
5
6
|
@property
def
info(
self
):
print
(
'the cup name is: %s size: %s type: %s'
%
(
self
.Name,
self
.Size,
self
.
Type
))
c1
=
Cup(
'mo'
,
'11'
,
'bf'
)
c1.info()
# 直接像这样调用会报错:TypeError: 'NoneType' object is not callable
|
# 正确调用方式: 静态属性,调用方式像数据属性一样调用就行。
1
2
3
4
5
6
7
8
|
@property
def
info(
self
):
return
'the cup name is: %s size: %s type: %s'
%
(
self
.Name,
self
.Size,
self
.
Type
)
c1
=
Cup(
'mo'
,
'11'
,
'bf'
)
print
(c1.info)
# 结果: the cup name is: mo size: 11 type: bf
|
# 类方法: 不跟具体实例捆绑,只跟类捆绑使用, 不需要实例化,直接用类就可以使用
1
2
3
4
5
6
7
8
9
10
11
12
|
class
Cup:
tags
=
1
@
classmethod
# 如果有类调用函数的需求那么直接使用类方法即可
def
tag_info(
cls
):
print
(
cls
)
Cup.tag_info()
print
(
'类方法调用: %s'
%
cls
.tags)
# 打印结果: <class '__main__.Cup'>
类方法调用:
1
|
# 类的工具包, 不跟类绑定,也不跟具体实例绑定
@staticmethod 跟类跟实例都没有关系
1
2
3
4
5
6
7
8
9
10
11
|
@staticmethod
def
info_sta(x,y):
print
(
'这里是类方式%s %s'
%
(x,y))
Cup.info_sta(
1
,
2
)
# 打印结果: 这里是类方式1 2
# 如果直接使用实例调用
c1
=
Cup(
'mo'
,
'11'
,
'bf'
)
c1.info_sta(
1
,
2
)
# 打印结果: 这里是类方式1 2 与直接类使用一样,但实际调用的只是这个方法的类的工具包
|
# 不带self,cls的调用方式与类工具包的区别
1
2
3
4
5
6
7
8
|
def
test2(x,y):
print
(
'这里是普通函数: %s %s'
%
(x,y))
Cup.test2(
3
,
2
)
# 打印结果: 这里是普通函数: 3 2
c1.test2(
3
,
2
)
# 实例会执行它里会调用c1的实例在传递到self中,这里这个函数只是普通函数方式
# 调用实例打印: TypeError: test2() takes 2 positional arguments but 3 were given
|
# 类工具与普通函数,使用字典查看它俩的区别
1
2
|
'info_sta'
: <
staticmethod
object
at
0x0000000002982198
>,
'test2'
: <function Cup.test2 at
0x000000000297F8C8
>
|
三大特性: 继承,封闭,派生
# 继承 子类会继承父类的所有属性,如果子父有相同的数据或方法,子类不会覆盖父类的任何数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
class
Up1:
# 先来个数据属性
cat
=
'Public'
# 再来个方法
def
__init__(
self
,name):
self
.name
=
name
def
Garage(
self
,name):
print
(
'这是一辆好车%s '
%
self
.name)
class
Down1(Up1):
pass
do
=
Down1(
'rolls-royce'
)
do.Garage(do)
print
(do.cat)
|
# 接口继承 ---------------------------------------------------------------------------------------
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
class
Alls:
def
eat(
self
):
pass
def
sleep(
self
):
pass
class
human(Alls):
def
eat(
self
):
print
(
'eat food'
)
# def sleep(self):
# print('slepp night')
h1
=
human()
h1.eat()
h1.sleep()
|
# 上面的代码只是看起来像接口,其实并没有起到接口的作用,子类完全可以不用去实现接口 ,这就用到了抽象类
# 接口继承、抽象类:定义一个基类,基类当中把自己的方法定义成接口函数,只要来一个子类,那么子类必须实现它的方法才能执行
1 什么是抽象类
与java一样,python也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化
2 为什么要有抽象类
如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。
比如我们有香蕉的类,有苹果的类,有桃子的类,从这些类抽取相同的内容就是水果这个抽象的类,你吃水果时,要么是吃一个具体的香蕉,要么是吃一个具体的桃子。。。。。。你永远无法吃到一个叫做水果的东西。
从设计角度去看,如果类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的。
从实现角度来看,抽象类与普通类的不同之处在于:抽象类中只能有抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接口有点类似,但其实是不同的,即将揭晓答案 来源于: http://www.cnblogs.com/linhaifeng/articles/7340153.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
import
abc
# 定义抽象层
class
Alls(metaclass
=
abc.ABCMeta):
@abc.abstractstaticmethod
#定义抽象方法,无需实现功能
def
eat(
self
):
pass
@abc.abstractstaticmethod
def
sleep(
self
):
pass
class
human(Alls):
#子类继承抽象类,但是必须定义eat和sleep方法
def
eat(
self
):
print
(
'eat food'
)
# def sleep(self):
# print('slepp night')
h1
=
human()
h1.eat()
h1.sleep()
# 打印结果:TypeError: Can't instantiate abstract class human with abstract methods sleep
# 父类中定义的方法,子类中必须要有,可以不实现功能,但是方法必须有以实现接口功能
|
# 继承顺序:
py2分深度和广度优先,
Py3只有广度优先
# 深度优先: 从左开始查找,一直到类的最深处
# 广度优先: 从左开始不找到最后,然后从右开始找到最后
MRO: 方向解析顺序
# 方法示例: F --> C --> B --> A
--> E --> D --> A
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
A:
def
test(
self
):
print
(
'A'
)
class
B(A):
def
test(
self
):
print
(
'B-a'
)
class
C(B):
def
test(
self
):
print
(
'C-B-A'
)
class
D(A):
def
test(
self
):
print
(
'D-A'
)
class
E(D):
def
test(
self
):
print
(
'E-D-A'
)
class
F(C,E):
# def test(self):
# print('F')
pass
f1
=
F()
f1.test()
# 打印结果为: C-B-A
|
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
# 继承顺序 查看 print(F.__mro__):
1
|
# (<class '__main__.F'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.D'>, <class '__main__.A'>, <class '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
|
class
cat:
def
__init__(
self
,name,high,
long
,wide):
self
.name
=
name
self
.high
=
high
self
.
long
=
long
self
.wide
=
wide
def
stat(
self
):
print
(
'%s 开动'
%
self
.name)
class
Public(cat):
# 这个self指的是这个类的__init__方法调用的self值,然后再传递到上层init中
def
__init__(
self
,name,high,
long
,wide,price):
cat.__init__(
self
,name,high,
long
,wide)
self
.price
=
price
def
stat(
self
):
cat.stat(
self
)
#public 开动
# public 这个车价格 10000
print
(
'%s 这个车价格 %s'
%
(
self
.name,
self
.price))
# 实例化这个对象
pu1
=
Public(
'public'
,
'210cm'
,
'600cm'
,
'400cm'
,
10000
)
# 执行类的方法
pu1.stat()
|
# supper(). 继承父类,使用直接supper().方法, 当父类名改变之后,只需要更改类的父类名,不需要更改代码的逻辑
# 使用supper 的好处: 当父类名更改时,程序代码不需要理性,并且不需要再传递self参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
class
Public(cat):
def
__init__(
self
,name,high,
long
,wide,price):
super
().__init__(name,high,
long
,wide)
# 这三种使用方式都是同一个意思
# super(Public,self).__init__(name,high,long,wide)
# super(__class__,self).__init__(name,high,long,wide)
# cat.__init__(self,name,high,long,wide)
self
.price
=
price
def
stat(
self
):
super
().stat()
# cat.stat(self) #public 开动
# public 这个车价格 10000
print
(
'%s 这个车价格 %s'
%
(
self
.name,
self
.price))
# 实例化这个对象
pu1
=
Public(
'public'
,
'210cm'
,
'600cm'
,
'400cm'
,
10000
)
# 执行类的方法
pu1.stat()
|
# 什么是多态: 由不同的类实例化得到的对象,调用同一个方法,执行的逻辑不同
# 多态的概念指出了对象如何通过他们共同的属性和动作来操作及访问,而不需考虑他们具体的类 如len
# 多态是由继承体现的一种方式
# 多态
# 类的继承有两层意义: 1、改变 2、扩展
# 多态的就是类的这两层意义的一个具体的实现机制即、调用不同的类实例化得对象下的相同的方法,实现的过程不一样
# python中的标准类型就是多态概念的一个很好的示范
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class
water:
def
__init__(
self
,name,temperature):
self
.name
=
name
self
.temperature
=
temperature
def
func(
self
):
if
self
.temperature <
0
:
print
(
'[%s] 会结冰'
%
self
.name)
elif
self
.temperature>
0
and
self
.temperature <
50
:
print
(
'[%s] 液化为水'
%
self
.name)
elif
self
.temperature >
51
:
print
(
'[%s] 成为水蒸气'
%
self
.name)
class
wa(water):
pass
w1
=
wa(
'水'
,
10
)
|
# 由不同的类实例化得到的对象,调用同一个方法,执行的逻辑不同
1
2
3
4
|
def
func(objs):
objs.func()
func(w1)
# w1.func() # 都是调用同一个方法,但执行的逻辑不同
|
# 封装
# class类似于口袋,数据属性跟方法就类似于口袋中的一些物品
1
2
3
4
5
6
7
8
9
10
|
class
pople:
data
=
'water'
def
__init__(
self
,
id
,name,addr):
self
.
id
=
id
self
.name
=
name
self
.addr
=
addr
def
start(
self
):
print
(
'社保ID: %s, 名字: %s, 住宅: %s'
%
(
self
.
id
,
self
.name,
self
.addr))
|
# 调用封装属性
1
2
3
4
5
|
from
Characteristic
import
pople
# 生成一个实例,并调用未知的属性
p1
=
pople(
'12321'
,
'xiong'
,
'天朝'
)
p1.start()
# 社保ID: 12321, 名字: xiong, 住宅: 天朝
|
# 约定规则,不要使用约定的隐藏属性
_ : python当中的约定,只要属性是单下划线下头的,就表示是一个隐藏属性, 外部无法调用,(其实外部可以调用,这只是py的一种约定,_表示不要使用这种属性内容)
1
|
print
(pople._data)
# _代表py与用户的约定,_表示这个是一个隐藏属性,用户不应该再使用它
|
__ : python会自动重命名为 _classname__属性名,
# 直接使用这个数据属性,一看认为它就是一个隐藏属性,无法调用其实不然
1
2
3
4
5
|
print
(pople.__data)
# AttributeError: type object 'pople' has no attribute '__data'
print
(pople.__dict__)
# 使用dict查看类的方法里,里面包含了 '_pople__data': 'water', py会自动重命名这个数据属性
print
(pople._pople__data)
# 使用重命名的再次查看也能查询到water
|