面向对象编程
获取对象信息
使用type()
判断对象类型 :type(对象)
用type()
来判断对象是否是函数
1 2 3 4 5 6 7 8 9 10 11 12 import typesdef 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))print (isinstance (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实例添加name
和age
属性:
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
对象加上width
和height
属性,以及一个只读属性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 EnumMonth = 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开始计数 。
练习题
把Student
的gender
属性改造为枚举类型,可以避免使用字符串:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from enum import Enum, uniqueclass 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,然后创建类,最后创建实例。在编写ORM框架时,所有的类只能动态定义,此时才会用metaclass来修改类