Python面向对象的编程过程中,我们为类实例化了对象,并通过对象指针来访问类中对应的资源,那么这些资源大体分为三大部分,分别是字段、方法和属性,我们将这三大块统称为类的成员。
一、字段
字段可以分为静态字段、动态字段,下面通过代码展示类中的两种字段
1
2
3
4
5
6
7
|
class
MyClass:
# 静态字段,属于类,多个对象共用一个静态字段
leader
=
"abuve"
def
__init__(
self
):
# 动态字段,属于对象,也可以叫普通的字段,每个对象都会有自己独有的动态字段
self
.name
=
"kevin"
|
动态字段在类的实例化过程中很常见,通过self为每个对象封装属于自己特有的数据,但如果类中全部采用动态字段,也会遇到一些不合理的弊端,例如下面代码:
1
2
3
4
5
6
7
8
9
10
11
|
class
Company:
def
__init__(
self
, dept, leader):
self
.company_name
=
"Center"
self
.dept
=
dept
self
.leader
=
leader
def
...
if
__name__
=
=
"__main__"
:
it_dept
=
Company(
"IT"
,
"Abuve"
)
hr_dept
=
Company(
"HR"
,
"Kevin"
)
|
我们通过动态字段方式为对象封装了自己独有的数据,但是这里发现公司名称company_name都为“Center”,不管创建哪个部门的对象,公司名称是不变的,我们知道动态字段存放在对象中,这样每个对象就都包含了一份company_name字段,这无疑增加了程序对内存的开销,因此更合理的方式应该使用静态字段,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
|
class
Company:
company_name
=
"Center"
def
__init__(
self
, dept, leader):
self
.dept
=
dept
self
.leader
=
leader
def
...
if
__name__
=
=
"__main__"
:
it_dept
=
Company(
"IT"
,
"Abuve"
)
hr_dept
=
Company(
"HR"
,
"Kevin"
)
|
同时在字段的调用方式上,我们也要遵循一些规则:
1、静态字段,属于类,通过类来调用访问
2、动态字段,属于对象,通过对象来调用访问
对于上述代码,我们通过下面的方式访问其中的字段数据:
1
2
3
|
print
it_dept.deptprint
hr_dept.leaderprint
Company.company_name
|
如果通过对象访问静态字段同样可以访问到数据,因为对象也是通过对象指针指向了自己的类,对象中没有的数据最终也会去类中查找,但是这样的调用方式并不合理。
1
2
|
# 通过对象调用,同样访问到了类的静态字段
print
it_dept.company_name
|
在字段前加入两个下划线,可以将该字段设置为私有字段,例如:
1
2
3
4
5
6
7
8
9
10
11
12
|
class
MyClass:
def
__init__(
self
, name):
self
.__name
=
name
def
show(
self
):
print
self
.__name
if
__name__
=
=
"__main__"
:
object
=
MyClass(
"Abuve"
)
# 通过对象无法访问到私有字段
print
object
.__name
# 私有字段通过类的内部方法访问
object
.show()
# 通过类名前加入下划线的方式同样可以访问到
print
object
._MyClass__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
|
class
MyClass:
def
__init__(
self
, name):
self
.name
=
name
# 普通方法
def
show(
self
):
return
self
.name
# 静态方法
@
staticmethod
def
detail(name):
print
'%s is good person.'
%
name
# 动态方法
@
classmethod
def
show_detail(
cls
):
cls
.detail(
'Kevin'
)
if
__name__
=
=
"__main__"
:
object
=
MyClass(
"Jack"
)
p_name
=
object
.show()
MyClass.detail(p_name)
MyClass.show_detail()
|
与字段一样,方法的调用上依然要遵循一些规则。
1、普通方法,由对象调用
2、静态方法,由类调用
3、类方法,属于静态方法的一种,通过类来调用,执行的时候会自动将类名传递进去,因此要有一个默认的接收参数。
静态方法依然也可以通过对象指针来访问到,但是这样调用并不合理,之所以将这种稍微特殊的方法写到类中,也是因为其与该类具备一定的相关性。
三、属性
如果说字段属于左派、方法属于右派,那么属性就属于中立派,因为它即具备方法的功能,同时又可以通过字段的方式来访问,下面为一段包含属性的代码段。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
class
PageSet:
def
__init__(
self
, count, page_size):
self
.count
=
count
self
.page_size
=
page_size
# 通过装饰器将page_num变为属性,对象调用时不需要再加括号
@
property
def
page_num(
self
):
page_count, remainder
=
divmod
(
self
.count,
self
.page_size)
if
remainder
=
=
0
:
return
page_count
else
:
return
page_count
+
1
if
__name__
=
=
"__main__"
:
# 传入条目总数,以及单页size大小
page_tools
=
PageSet(
108
,
10
)
# 像访问字段一样执行了page_num方法
print
page_tools.page_num
|
上面的代码实现了一个分页设置,我们通过装饰器property将page_num方法变为属性,那么该方法在被对象调用时,就像访问字段一样,不需要再加括号了。此时我们只实现了通过字段的方式来访问方法,通过下面的代码,我们也可以为属性调用相关的赋值、删除动作。
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
PageSet:
def
__init__(
self
, count, page_size):
self
.count
=
count
self
.page_size
=
page_size
@
property
def
page_num(
self
):
page_count, remainder
=
divmod
(
self
.count,
self
.page_size)
if
remainder
=
=
0
:
return
page_count
else
:
return
page_count
+
1
@page_num.setter
def
page_num(
self
, value):
print
value
print
'This is set function.'
@page_num.deleter
def
page_num(
self
):
print
'This is delete function.'
if
__name__
=
=
"__main__"
:
page_tools
=
PageSet(
108
,
10
)
# 调用property修饰属性
page_tools.page_num
# 调用page_num.setter修饰属性
page_tools.page_num
=
12
# 调用page_num.deleter修饰属性
del
page_tools.page_num
|
四、特殊成员
特殊成员指函数两边都带有下划线的特殊方法,这些特殊方法为类提供独有的功能。
1、__init__
构造方法,这类方法最为常见,在我们实例化类的时候,就是通过__init__构造方法封装了对象的数据。
2、 __del__
析构函数,通过__del__函数构造特定功能,在为对象执行del操作时,可以自动调用该部分代码,在程序执行相关垃圾回收时,可以应用析构方法。
3、__doc__
注释,通过对象,可以访问到__doc__函数中指定的注释内容。
4、__module__
通过该方法可以显示当前对象属于哪个模块。
5、__class__
通过该方法可以显示当前对象属于哪个类。
6、__call__
如果我们在类的实例化对象后面加括号时,会自动执行类中的call方法。
1
2
3
4
5
6
7
|
class
MyClass:
def
__call__(
self
):
print
'This is something...'
if
__name__
=
=
"__main__"
:
object
=
MyClass()
object
()
|
7、__str__
默认打印对象时,只能够显示内存地址,通过__str__可以显示想要返回的内容。
1
2
3
4
5
6
7
|
class
MyClass:
def
__str__(
self
):
return
'This is text that I want to return...'
if
__name__
=
=
"__main__"
:
object
=
MyClass()
print
object
|
8、__add__
可以将两个对象中的内容进行相加。
1
2
3
4
5
6
7
8
9
10
11
|
class
MyClass:
def
__init__(
self
, company, ceo):
self
.company
=
company
self
.ceo
=
ceo
def
__add__(
self
, other):
return
"%s---%s"
%
(
self
.company, other.ceo)
obj1
=
MyClass(
"A"
,
"Abuve"
)
obj2
=
MyClass(
"B"
,
"Kevin"
)
print
obj1
+
obj2
|
代码最终打印了 "A---Kevin"
9、__dict__
对象调用该方法,可以打印出所有封装的数据,类调用该访问可以打印出所有方法。
10、__getitem__、__setitem__、__delitem__
通过字典的方式操作对象,可以为其设置相应的执行动作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class
MyClass(
object
):
def
__getitem__(
self
, key):
print
'__getitem__'
,key
def
__setitem__(
self
, key, value):
print
'__setitem__'
,key,value
def
__delitem__(
self
, key):
print
'__delitem__'
,key
if
__name__
=
=
"__main__"
:
obj
=
Myclass()
result
=
obj[
'k1'
]
# 执行了__getitem__方法
obj[
'k2'
]
=
'abuve'
# 执行了__setitem__方法
del
obj[
'k1'
]
# 执行了__delitem__方法
|
11、__iter__
用于迭代器,返回一个可以被迭代的对象
1
2
3
4
5
6
7
8
|
class
MyClass(
object
):
def
__iter__(
self
):
return
iter
([
1
,
2
,
3
,
4
,
5
])
if
__name__
=
=
"__main__"
:
obj
=
MyClass()
for
i
in
obj:
print
i
|
12、isinstance/issubclass
通过isinstance可以判断某个对象的类型,issubclass可以判断某两个类是否为继承关系
1
2
3
4
5
6
7
8
9
|
class
Class1():
pass
class
Class2(Class1):
pass
if
__name__
=
=
"__main__"
:
obj
=
Class2()
# 判断obj的类型是否为Class2
print
isinstance
(obj, Class2)
# isinstance同样也可以判断是否继承自父类
print
isinstance
(obj, Class1)
print
issubclass
(Class2, Class1)
|