唐抉的个人博客

python学习笔记(七)

字数统计: 2k阅读时长: 8 min
2022/10/11

面向对象编程

获取对象信息

使用type()

判断对象类型type(对象)

type()判断对象是否是函数

1
2
3
4
5
6
7
8
9
10
11
12
import types
def fn():
pass
print(type(fn)==types.FunctionType)
print(type(abs)==types.BuiltinFunctionType)
print(type(lambda z:z)==types.LambdaType)
print(type((x for x in range(10)))==types.GeneratorType)
#运行结果如下:
True
True
True
True

使用isinstance()

判断class的类型isinstance(变量名,对象名)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Person(object):
def __init__(self,num=0,sex='m'):
self.num=num
self.sex=sex
def getnum(self):
return self.num
class Student(Person):
def setnum(self,num):
self.num=num
a=Person()
b=Student()
print(isinstance(a,Person))
print(isinstance(b,Person))#b继承了a,所以b也是Person类型
print(isinstance(a,Student))#a不是Student类型
#运行结果如下:
True
True
False

isinstance也可以用来判断基本类型,形式为:isinstance(判断的对象,类型名)

isinstance还可以判断一个变量是否是某些类型的一种,例如判断是否是list或tuple:

1
2
3
4
5
6
7
8
9
print(isinstance([1,2,3],tuple))
print(isinstance((1,2,3),tuple))
print(isinstance([1,2,3],(list,tuple)))
print(isinstance((1,2,3),(list,tuple)))
#运行结果如下:
False
True
True
True

因此优先使用isinstance()判断类型,可以将指定类型及其子类”一网打尽“。

使用dir()

只有在不知道对象信息时,才会用以下函数去获取对象信息。

获得一个对象的所有属性和方法:dir(),其返回一个包含字符串的list。例如:

1
2
3
print(dir(123))
#运行结果如下:
['__abs__', '__add__', '__and__',..., 'real', 'to_bytes']

__xxx__为有特殊用途的属性和方法,其他的为普通属性和方法。

判断对象是否有某属性或方法:hasattr(指向对象的变量名,属性或方法)

设置对象的属性值:setattr(指向对象的变量名,属性,属性值)

获取对象的属性或方法:getattr(指向对象的变量名,属性或方法)

实例属性和类属性

通过实例变量或self变量给实例绑定的属性是实例属性,归当前实例所有。

直接在class中定义的属性是类属性,归类所有,但类的所有属性都可以访问,用类名.属性名调用。

两者的形式区别如下所示:

1
2
3
4
class Person(object):
num='123'#类属性
def __init__(self,sex):
self.sex=sex#实例属性

练习题

为了统计学生人数,可以给Student类增加一个类属性,每创建一个实例,该属性自动增加:

代码如下:

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(object):
count = 0
def __init__(self, name):
self.name = name
Student.count=Student.count+1

# 测试:
if Student.count != 0:
print('测试失败!')
else:
bart = Student('Bart')
if Student.count != 1:
print('测试失败!')
else:
lisa = Student('Bart')
if Student.count != 2:
print('测试失败!')
else:
print('Students:', Student.count)
print('测试通过!')
#运行结果如下:
Students: 2
测试通过!

面向对象高级编程

使用__slots__

定义一个特殊的变量来限制对实例添加属性:__slots__

例如,只允许对Student实例添加nameage属性:

1
2
class Student(object):
__slots__=('name','age')

__slots__定义的属性仅对当前类实例起作用,对继承的子类不起作用

使用@property

把一个方法变成属性调用:@property

如把一个getter方法变成属性,只需要加上@property就可以了。与此同时,@property本身又创建了另一个@实例名.setter,负责把一个setter方法变成属性赋值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Student(object):
@property
def score(self):
return self.score

@score.setter
def score(self,value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value>100 or value<0:
raise ValueError('score must between 0~100!')
self.score=value
s=Student()
s.score=299
print(s.score)
#运行结果如下:
Traceback (most recent call last):
File "main.py", line 14, in <module>
s.score=299
File "main.py", line 11, in score
raise ValueError('score must between 0~100!')
ValueError: score must between 0~100!

@property还可以定义只读属性。只定义getter方法,不定义setter方法便是一个只读属性,如下面的score属性便是只读属性:

1
2
3
4
class Student(object):
@property
def score(self):
return self.name

注意:属性的方法名不要与实例变量重名。在调用方法时会首先转换为方法调用,而在return语句时,又看作访问类的属性,于是又转换为方法调用,从而导致栈溢出报错。

练习题

请利用@property给一个Screen对象加上widthheight属性,以及一个只读属性resolution

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
class Screen(object):
@property
def width(self):
return self._width
@width.setter
def width(self,value):
self._width=value

@property
def height(self):
return self._height
@height.setter
def height(self,value):
self._height=value

@property
def resolution(self):
return self._height*self._width
# 测试:
s = Screen()
s.width = 1024
s.height = 768
print('resolution =', s.resolution)
if s.resolution == 786432:
print('测试通过!')
else:
print('测试失败!')
#运行结果如下:
resolution = 786432
测试通过!

多重继承

多重继承形式:class 类名(基类1,基类2):

在设计类的继承关系时,子类除了继承基类1外,再同时继承基类2,这种设计称之为Mixln。Mixln可以给一个类增加多个功能。只允许单一继承的语言(如java),不能使用Mixln的设计。

定制类

pytho的class中有很多类似于__slots____len__的函数,可以帮助定制我们类。下面是最常用的几个定制方法:

返回一个给用户看的字符串:__str__(self)

返回一个程序开发者看的字符串__repr__(self),其是为调试服务的。

返回一个迭代对象:__iter__(self),可以作用于for循环

返回一个迭代对象:__getitem__(self,n),可以作用于list。其传入的参数可能是一个int,也可能是一个切片对象slice

动态返回一个属性:__getattr__(self,属性名),只有在没有找到属性的情况下才调用。

直接调用实例:__call__(self)

判断一个对象是否是可调用对象:callable(对象)

使用枚举类

定义一个class类型,其每一个常量都是class的一个唯一实例,枚举类(Enum类)便可以实现这个功能。Enum可以把一组相关常量定义在一个class中,且class不可变,其成员可以直接比较。

如下代码所示,可以直接使用Month.Jan.value来引用一个常量,或者枚举它是所有成员。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from enum import Enum
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
print(Month.Jan.value)#
for name,member in Month.__members__.items():
print(name,'=>',member,',',member.value)
#运行结果如下:
1
Jan => Month.Jan , 1
Feb => Month.Feb , 2
Mar => Month.Mar , 3
Apr => Month.Apr , 4
May => Month.May , 5
Jun => Month.Jun , 6
Jul => Month.Jul , 7
Aug => Month.Aug , 8
Sep => Month.Sep , 9
Oct => Month.Oct , 10
Nov => Month.Nov , 11
Dec => Month.Dec , 12

value属性是自动赋给成员的int常量,默认从1开始计数

练习题

Studentgender属性改造为枚举类型,可以避免使用字符串:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from enum import Enum, unique
class Gender(Enum):
Male=Enum('Male',('Male','Female'))

class Student(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender

# 测试:
bart = Student('Bart', Gender.Male)
if bart.gender == Gender.Male:
print('测试通过!')
else:
print('测试失败!')
#运行结果如下:
测试通过!

使用元类

type()

查看一个类型或变量的类型:type(类名,(基类1,基类2,...),通过type()函数创建出的类,可将函数绑定到方法上。

例如通过type()函数创建出Hello类:Hello=type('Hello',(object,),dict(hello=fn)),把fn函数绑定到方法名hello上。

metaclass

正常创建实例的步骤是先定义类,然后根据这个类创建实例。若想先创建出类,可以通过元类(metaclass)创建,即先定义metaclass,然后创建类,最后创建实例。在编写ORM框架时,所有的类只能动态定义,此时才会用metaclass来修改类

CATALOG
  1. 1. 面向对象编程
    1. 1.1. 获取对象信息
      1. 1.1.1. 使用type()
      2. 1.1.2. 使用isinstance()
      3. 1.1.3. 使用dir()
    2. 1.2. 实例属性和类属性
      1. 1.2.1. 练习题
  2. 2. 面向对象高级编程
    1. 2.1. 使用__slots__
    2. 2.2. 使用@property
      1. 2.2.1. 练习题
    3. 2.3. 多重继承
    4. 2.4. 定制类
    5. 2.5. 使用枚举类
      1. 2.5.1. 练习题
    6. 2.6. 使用元类
      1. 2.6.1. type()
    7. 2.7. metaclass