0%

4. 类与对象

Python 原创基础教程

第四章 类与对象

4.1 类简述

  • 类是一个“模版”,可以包含变量函数(即类是属性和方法的集合)
  • 对象是类创建的实例,通过实例可以调用变量和函数
  • 类中普通函数的必须有第一个参数,来代表该类调用函数的具体对象(一般取名为self)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#类的基本语法
class A:
x = 'x' #静态变量
def __init__(self): #构造函数
self.y = 'y' #普通变量

def f1(self): #普通函数,无参数
print("aaa")

def f2(self,name): #普通函数,有参数
print('my name is %s'%name)

obj1 = A() #实例化对象,对象变量为obj1
obj1.f1() #对象调用函数f1()

4.2 类成员

类的结构图,其中变量也称属性,函数也称方法

1
2
3
4
5
6
7
8
9
10
graph LR
A(类成员)-->B(变量)
A-->C(函数)
A-->D(方法属性)
B-->普通变量
B-->静态变量
C-->普通函数
C-->类函数
C-->静态函数

4.3 变量(属性)

类中的变量也叫属性,亦可叫字段,是类这种数据结构中保存多样数据的方式

  • 普通字段属于对象(每个对象都存一份)
  • 静态字段属于类(就类存有一份)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Province:
#静态字段
country = '中国'

def __init__(self,name):
#普通字段
self.name=name


obj = Province('河北省')
访问普通字段
print obj.name

访问静态字段
Province.country

4.4 函数(方法)

函数在类中也叫方法。通过方法,让类这种数据结构具有“运动”的特征,即类不仅可以保存数据,以可以操作处理数据,其中操作处理数据就是通过类方法实现。

  • 普通方法:对象调用,至少一个self参数,执行方法自动调用具体对象给该方法
  • 类方法:由类调用,至少一个cls参数,执行时自动调用cls给该方法
  • 静态方法:由类调用,无默认参数

三种方法都存于类

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

#普通方法
def ord_func(self):
print(普通方法)

#类方法
@classemethod
def class_func(cls):
print(类方法)

#定义静态方法
@staticmethod
def static_func():
print(静态方法)

#调用普通方法
f = Foo()
f.ord_func()

#调用类方法
Foo.class_func()

#调用静态方法
Foo.static_func()

4.4.1 函数属性化

通过装饰器,函数可以像属性一样调用

  • 用@property装饰器定义
  • 仅有一个self参数
  • 调用时无需括号

经典类(python2中常用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
################ 定义 ###############
class Foo:

def func(self):
pass

# 定义属性
@property
def prop(self):
print('abc')
################ 调用 ###############
foo_obj = Foo()

foo_obj.func()
foo_obj.prop #调用属性

新式类

由于新式类中具有三种访问方式,我们可以根据他们几个属性的访问特点,分别将三个方法定义为对同一个属性:

  • 获取
  • 修改
  • 删除
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 Goods(object):

@property
def price(self):
print '@property'

@price.setter
def price(self, value):
print '@price.setter'

@price.deleter
def price(self):
print '@price.deleter'

# ############### 调用 ###############
obj = Goods()

obj.price # 自动执行 @property 修饰的 price 方法,并获取方法的返回值

obj.price = 123 # 自动执行 @price.setter 修饰的 price 方法,并将 123 赋值给方法的参数

del obj.price # 自动执行 @price.deleter 修饰的 price 方法

以下是property的构造方法(静态变量的方式)中有个四个参数

  • 第一个参数是方法名,调用 对象.属性 时自动触发执行方法
  • 第二个参数是方法名,调用 对象.属性 = XXX 时自动触发执行方法
  • 第三个参数是方法名,调用 del 对象.属性 时自动触发执行方法
  • 第四个参数是字符串,调用 对象.属性.doc ,此参数是该属性的描述信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Foo

def get_bar(self):
return 'wupeiqi'

# *必须两个参数
def set_bar(self, value):
return return 'set value' + value

def del_bar(self):
return 'wupeiqi'

BAR = property(get_bar, set_bar, del_bar, 'description...')

obj = Foo()

obj.BAR # 自动调用第一个参数中定义的方法:get_bar
obj.BAR = "alex" # 自动调用第二个参数中定义的方法:set_bar方法,并将“alex”当作参数传入
del Foo.BAR # 自动调用第三个参数中定义的方法:del_bar方法
obj.BAE.__doc__ # 自动获取第四个参数中设置的值:description...

4.5 类变量(属性)扩展

  • 静态变量(保存在类中):在任何对方都能访问
  • 私有静态变量(变量前加__):只有在类的内部能访问
  • 普通变量(在函数里的变量):对象可以访问、类内部可以访问、派生类中可以访问
  • 普通私有变量(变量前加__):仅类内部能访问
  1. 静态变量:类可以访问、类内部可以访问、派生类中可以访问
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class C:

name = "全局变量"

def func(self):
print C.name

class D(C):

def show(self):
print C.name

C.name # 类访问

obj = C()
obj.func() # 类内部可以访问

obj_son = D()
obj_son.show() # 派生类中可以访问

2.私有静态变量:仅类内部可以访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class C:

__name = "私有静态变量"

def func(self):
print C.__name

class D(C):

def show(self):
print C.__name

C.__name # 类访问 错误

obj = C()
obj.func() # 类内部可以访问 正确

obj_son = D()
obj_son.show() # 派生类中可以访问 错误

3.普通变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class C:

def __init__(self):
self.foo = "普通变量"

def func(self):
print self.foo  # 类内部访问

class D(C):

def show(self):
print self.foo # 派生类中访问

obj = C()

obj.foo # 通过对象访问
obj.func() # 类内部访问

obj_son = D();
obj_son.show() # 派生类中访问

4.私有字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class C:

def __init__(self):
self.__foo = "私有字段"

def func(self):
print self.foo  # 类内部访问

class D(C):

def show(self):
print self.foo # 派生类中访问

obj = C()

obj.__foo # 通过对象访问 错误
obj.func() # 类内部访问 正确

obj_son = D();
obj_son.show() # 派生类中访问 错误

如果想要强制访问私有字段,可以通过 【对象._类名__私有字段明 】访问(如:obj._C__foo),不建议强制访问私有成员。

4.6 类的特殊成员

类的特殊成员包括特殊变量和特殊方法,其是系统定义的,不需要用户定义,一般具有普通变量和方法所不具有的特殊功能

1.__ doc__:表示类的描述

1
2
3
4
5
6
7
8
class Foo:
""" 描述类信息,这是用于看片的神奇 """

def func(self):
pass

print(Foo.__doc__)
#输出:类的描述信息

2.__ module__:表示当前操作对象在哪个模块

模块:组织函数和类的单位(类似java的一类一文件,有时不同模块可能有同名函数),例如Django

1
print(obj.__module__) #输出对象所在模块

3.__ class__:表示当前操作对象的是什么

1
print(obj.__class__) #输出对象所在类

4.__ init(self,..)__:
构造方法、类创建对象时自动执行

5.__ del(self,..)__:
析构方法、当对象在内存释放时、自动触发执行。

此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。

6.__ call(self,..)__:
除析构方法外,执行对象的另一种特殊方法,对象加括号执行

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

def __init__(self):
pass

def __call__(self, *args, **kwargs):

print '__call__'

obj = Foo() # 执行 __init__
obj() # 执行 __call__

7.__ dict__:
显示类(静态变量、方法)和对象(普通字段)的所以成员

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

country = 'China'

def __init__(self, name, count):
self.name = name
self.count = count

def func(self, *args, **kwargs):
print 'func'

# 获取类的成员,即:静态字段、方法、
print Province.__dict__
# 输出:{'country': 'China', '__module__': '__main__', 'func': , '__init__': , '__doc__': None}

obj1 = Province('HeBei',10000)
print obj1.__dict__
# 获取 对象obj1 的成员
# 输出:{'count': 10000, 'name': 'HeBei'}

obj2 = Province('HeNan', 3888)
print obj2.__dict__
# 获取 对象obj1 的成员
# 输出:{'count': 3888, 'name': 'HeNan'}

  1. __ str__:如果一个类中定义了__str__方法,那么在打印(print)对象时,默认输出该方法的返回值。

9.__ getitem__、__ setitem__、__ delitem__

用于对象的索引操作,如字典。以上分别表示获取、设置、删除数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env python
# -*- coding:utf-8 -*-

class Foo(object):

def __getitem__(self, key):
print '__getitem__',key

def __setitem__(self, key, value):
print '__setitem__',key,value

def __delitem__(self, key):
print '__delitem__',key

obj = Foo()

result = obj['k1'] # 自动触发执行 __getitem__
obj['k2'] = 'wupeiqi' # 自动触发执行 __setitem__
del obj['k1'] # 自动触发执行 __delitem__

10.getslice__、__setslice__、__delslice

用于对象的分片操作(如列表)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

class Foo(object):

def __getslice__(self, i, j):
print '__getslice__',i,j

def __setslice__(self, i, j, sequence):
print '__setslice__',i,j

def __delslice__(self, i, j):
print '__delslice__',i,j

obj = Foo()

obj[-1:1] # 自动触发执行 __getslice__
obj[0:1] = [11,22,33,44] # 自动触发执行 __setslice__
del obj[0:2] # 自动触发执行 __delslice__

11.__ iter__:迭代器

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
##case 1:

class Foo(object):
pass

obj = Foo()

for i in obj:
print i

# 报错:TypeError: 'Foo' object is not iterable

## case 2:

class Foo(object):

def __iter__(self):
pass

obj = Foo()

for i in obj:
print i

# 报错:TypeError: iter() returned non-iterator of type 'NoneType'

## case 3:

class Foo(object):

def __init__(self, sq):
self.sq = sq

def __iter__(self):
return iter(self.sq)

obj = Foo([11,22,33,44])

for i in obj:
print i

迭代的变种

1
2
3
4
5
6
7
8
9
10
11
obj = iter([11,22,33,44])

for i in obj:
print i

############################
obj = iter([11,22,33,44])

while True:
val = obj.next()
print val

4.7 旧式类和新式类

从Python2.2开始,Python 引入了 new style class(新式类)

  • 新式类类似Java,有一个根类object,所有类都继承自根类object
  • 新式类跟经典类的差别主要是以下几点:

a.新式类对象可以直接通过__class__属性获取自身类型:type

b.继承搜索的顺序发生了改变,经典类多继承属性搜索顺序: 先深入继承树左侧,再返回,开始找右侧;新式类多继承属性搜索顺序: 先水平搜索,然后再向上移动

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
# -*- coding:utf-8 -*-  
class A(object):
"""
新式类
作为所有类的基类
"""
def foo(self):
print "class A"

class A1():
"""
经典类
作为所有类的基类
"""
def foo(self):
print "class A1"

class C(A):
pass

class C1(A1):
pass

class D(A):
def foo(self):
print "class D"

class D1(A1):
def foo(self):
print "class D1"



class E(C, D):
pass

class E1(C1, D1):
pass

e = E()
e.foo()


e1 = E1()
e1.foo()

c.新式类增加了__slots__内置属性,可以把实例属性的种类锁定到__slots__规定的范围之中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#比如只允许对A实例添加name和age属性
# -*- coding:utf-8 -*-

class A(object):
__slots__ = ('name', 'age')

class A1():
__slots__ = ('name', 'age')

a1 = A1()
a = A()

a1.name1 = "a1"
a.name1 = "a"

#A是新式类添加了__slots__ 属性,所以只允许添加 name age

#A1经典类__slots__ 属性没用

d.新式类增加了__getattribute__方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class A(object):  
def __getattribute__(self, *args, **kwargs):
print "A.__getattribute__"


class A1():
def __getattribute__(self, *args, **kwargs):
print "A1.__getattribute__"


a1 = A1()
a = A()

a.test
print "========="
a1.test

#A是新式类,每次通过实例访问属性,都会经过__getattribute__函数,

#A1不会调用__getattribute__所以出错了

4.8 类的实例化

  • __call__方法

类的实例化主要通过__init__()函数和__call__()函数实现,其中__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
class MyType(type):

def __init__(self, what, bases=None, dict=None):
super(MyType, self).__init__(what, bases, dict)

def __call__(self, *args, **kwargs):
obj = self.__new__(self, *args, **kwargs)

self.__init__(obj)

class Foo(object):

__metaclass__ = MyType

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

def __new__(cls, *args, **kwargs):
return object.__new__(cls, *args, **kwargs)

# 第一阶段:解释器从上到下执行代码创建Foo类
# 第二阶段:通过Foo类创建obj对象
obj = Foo()
  • __new__方法

继承自object的新式类才有__new__

__new__方法接受的参数虽然也是和__init__一样,但__init__是在类实例创建之后调用,而 __new__方法正是创建这个类实例的方法。

依照Python官方文档的说法,__new__方法主要是当你继承一些不可变的class时(比如int, str, tuple), 提供给你一个自定义这些类的实例化过程的途径。还有就是实现自定义的metaclass。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 创建一个类,继承int,并返回绝对值
class PositiveInteger(int):
def __init__(self, value):
super(PositiveInteger, self).__init__(self, abs(value))

i = PositiveInteger(-3)
print i
#结果还是-3

class PositiveInteger(int):
def __new__(cls, value):
return super(PositiveInteger, cls).__new__(cls, abs(value))

i = PositiveInteger(-3)
print i
#结果是 3

因为类的每一次实例化都是通过__new__实现的,通过重载类来实现单例

1
2
3
4
5
6
7
8
9
10
11
12
13
class Singleton(object):
def __new__(cls):
# 关键在于这,每一次实例化的时候,我们都只会返回这同一个instance对象
if not hasattr(cls, 'instance'):
cls.instance = super(Singleton, cls).__new__(cls)
return cls.instance

obj1 = Singleton()
obj2 = Singleton()

obj1.attr1 = 'value1'
print obj1.attr1, obj2.attr1
print obj1 is obj2