类是一种数据结构,,可以包含数据成员和函数成员。在程序中可以定义类,并创建和使用其对象实例。
面向对象的程序设计具有3个基本特征,即封装、继承和多态,可以大大增加程序的可靠性代码的可重用性和程序的可维护性,从而提高程序的开发效率。
所谓的对象(object),从概念层面讲,就是某种事物的抽象(功能)。抽象包括数据抽象和过程抽象两个方面:数据抽象就是定义对象的属性;过程抽象就是定义对象的操作。
面向对象的程序设计强调把数据(属性)和操作(服务)结合为一个不可分的系统单位(即对象),在对象的外部只需要知道它做什么,而不必知道它如何做。
从规格层面讲,对象是一-序列可以被其他对象使用的公共接口(对象交互)。从语言实现层面来看,对象封装了数据和代码(数据和程序)。
封装(encapsulation)是面向对象的主要特性。所谓封装,也就是把客观事物抽象并封装成对象,即将数据成员、属性、方法和事件等集合在一个整体内。通过访问控制还可以隐藏内部成员,但只允许可信的对象访问或操作自己的部分数据或方法。
封装保证了对象的独立性,可以防止外部程序破坏对象的内部数据,同时便于对程序的维护和修改。
继承(neritance)是面向对象的程序设计中代码重用的主要方法。继承允许使用现有类的功能,并在无须重新改写原来类的情况下对这些功能进行扩展。继承可以避免代码复制和相关的代码维护等问题。
派生类具有基类的所有非私有数据和行为以及新类自己定义的所有其他数据和行为,即子类具有两个有效类型:子类的类型及其继承的基类的类型。对象可以表示多个类型的能力称为多态性(polymorphism)。
多态性允许每个对象以自己的方式去响应共同的消息,从而允许用户以更明确的方式建立通用软件,提高软件开发的可维护性。
类是一个数据结构,类定义数据类型的数据(属性)和行为(方法)。对象是类的具体实体也可以称之为类的实例(instance)。
在Python语言中,类称为类对象(class object); 类的实例称为实例对象(instance obiect).
类使用关键字class声明。类的声明格式如下:
class类名:
类体
其中,类名为有效的标识符,一般为多个单词组成的名称每个单词除第一个字母大写外,其余的字母均小写;类体由缩进的语句块组成。
定义在类体内的元素都是类的成员。类的主要成员包括两种类型,即描述状态的数据成员(属性)和描述操作的函数成员(方法)。
class语句实际上是Python的复合语句,Python解释器解释执行class语句时会创建一个类对象。
[例 1] 创建类对象。
class Person1: #定义类Person1
pass #类体为空语句
#测试代码
p1 = Person1() #创建和使用对象
print(Person1, type(Person1), id(Person1))
print(p1, type(p1), id(p1))
类是抽象的,如果要使用类定义的功能,就必须实例化类,即创建类的对象。在创建实例对象后,可以使用“.”运算符来调用其成员。
注意:创建类的对象、创建类的实例、实例化类等说法是等价的,都是以类为模板生成了一个对象。
实例对象的创建和调用格式如下。
anObject =类名(参数列表)
an0bject.对象函数或anbject.对象属性
Python创建实例对象的方法无须使用关键字new,而是直接像调用函数一样调用类对像并传递参数,因此类对象是可调用对象(Callable).在Pyhon内置函数中bool, int, str, list, dict, set等均为可调用内置类对象。
在有的场合也称之为函数,例如使用str()函数把数值123转换为字符串的形式为str(123)。
[例 2] 实例对象的创建和使用示例。
c1 = complex(1, 2)
c1 conjugate() #输出:(1-2j)
cl.real #输出:1.0
说明:语句cl元complex(1, 2)创建类complex的实例对象并绑定到变量cl;表达式cl. conjugate()调用实例对象cl的conjugate()方法,返回其共轭值(1-2j);表达式cl.real引用实例对象cl的实部,返回值1.0。
类的数据成员是在类中定义的成员变量(域),用来存储描述类的特征的值称之为属性。
属性可以被该类中定义的方法访问,也可以通过类对象或实例对象进行访问。在丽数体或代码块中定义的局部变量只能在其定义的范围内进行访问。
属性实际上是在类中的变量。Python 变量不需要声明,可直接使用。建议用户在类定义的开始位置初始化类属性,或者在构造函数(init()中 初始化实例属性。
通过“self.变量名”定义的属性称为实例对象属性,也称为实例对象变量。类的每个实例都包含了该类的实例对象变量的一-个单独副本,实例对象变量属于特定的实例。实例对象变量在类的内部通过self访问,在外部通过对象实例访问。
实例对象属性一般在__init__()方法中通过如下形式初始化:
self.实例变量名=初始值
然后,在其他实例函数中通过self 访问:
self.实例变量名=值 #写入
self.实例变量名 #读取
或者,在创建对象实例后通过对象实例访问:
#创建对象实例
obj1 =类名()
#写人
obj1.实例变量名=值
#读取
obj1.实例变量名
[例 3] 定义类Person2定义实例属性。
class Person2: #定义类Person2
def __init__(self, name,age): #__init__方法
self.name = name #初始化self.name,即成员变量name(域)
self.age = age #初始化self.age,即成员变量age(域)
def say_hi (self): #定义类Person2的函数sayHi
print('您好, 我叫', self.name) #在实例方法中通过self.name读取成员变量name(域)
#测试代码
p1 = Person2('张三',25) #创建对象
p1. say_hi () #调用对象的方法
print(p1.age) #通过p1.age(obj1.变量名)读取成员变量age(域)
在Python中也允许声明属于类对象本身的变量,即类对象属性,也称之为类对象变量静态属性。类属性属于整个类,不是特定实例的一部分,而是所有实例之间共享一个副本,类对象属性一般在类体中通过如下形式初始化:
类变量名=初始值
然后,在其类定义的方法中或外部代码中通过类名访问:
类名.类变量名=值 #写人
类名.类变量名 #读取
[例 4] 定义类对象属性。
class Person3:
count = 0 #定义属性count,表示计数
name = "Person" #定义属性name1,表示名称
#测试代码
Person3.count += 1 #通过类名访问,将计数加1
print(Person3.count) #类名访问,读取并显示类属性
print(Person3.name) #类名访问,读取并显示类属性
p1 = Person3() #创建实例对象1
p2 = Person3() #创建实例对象2
print((p1.name, p2.name)) #通过实例对象访问,读取成员变量的值
Person3.name = "雇员" #通过类名访问,设置类属性值
print((p1.name, p2.name)) #读取成员变量的值
p1.name = "员工" #通过实例对象访问,设置实例对象成员变量的值
print((p1.name, p2.name)) #读取成员变量的值
程序运行结果如下。
Person
( ‘Person’, ‘Person’)
(雇员,雇员)
(员工’,‘雇员’)
说明:类属性如果通过“obj.属性名”来访问,则属于该实例的实例属性。虽然类属性可以使用对象实例来访问,但容易造成困惑,所以建议用户不要这样使用,而是使用标准的访问方式“类名.类变量名”。
Python类的成员没有访问控制限制,这与其他面向对象的语言不同。
通常约定以两个下画线开头,但是不以两个下画线结束的属性是私有的(private),其他为公共的(pubic)。注意,不能直接访问私有属性但可以在方法中访问。
[例 5]私有属性示 例 。
class A:
__name = 'class A' #私有类属性
def get_name():
print(A.__name) #在类方法中访问私有类属性
#测试代码
A.get_name()
A.__name #导致错误,不能直接访问私有类属性
面向对象编程的封装性原则要求不直接访问类中的数据成员。在Python中可以定义私有属性然后定义相应的访问该私有属性的两数,并使用@property装饰器来装饰这些函数。程序可以把函数“当作"属性访问,从而提供更加友好的访问方式。
[例 6] property 装饰器示例1 。
class Person11:
def __init__(self, name):
self.__name = name
@property
def name(self):
return self.__name
#测试代码
p = Person11('王五')
print(p.name)
程序运行结果如下。
王五
@property装饰器默认提供-个只读属性,如果需要.可以使用对应的getter , setter和deleter装饰器实现其他访问器函数。
[例 7] property装饰器示例2 。
class Person12:
def __init__(self, name):
self.__name = name
@property
def name(self):
return self.__name
@name.setter
def name(self, value):
self.__name = value
@name.deleter
def name(self):
del self.__name
#测试代码
p = Person12('姚六')
p.name = '王依依'
print(p.name)
程序运行结果如下。
王依依
property的调用格式如下。
property(fget = None, fset = None, fdel= None, doc = None)
其中,fget为get访问器; fset 为set 访问器; fdel 为del访问器。
[例 8] property 装饰器示例3 。
class Person13:
def __init__(self, name):
self.__name = name
def getname(self):
return self.__name
def setname(self, value):
self.__name = value
def delname(self):
del self.__name
name = property(getname, setname, delname, "I'm the 'name' property.")
#测试代码
p = Person13('爱丽丝');print(p.name)
p.name = '罗伯特'; print(p.name)
程序运行结果如下。
爱丽丝
罗伯特
在Pyhon对象中包含许多以双下画线开始和结束的属性,称之为特殊属性。常用的殊属性如表9-1所示。假设示例基于i=123。
在Python中,可以赋予一个对象自定义的属性,即类定义中不存在的属性。对象通过特殊__属性diet 存储自定义属性。通过重载__getattr ()和__setattr__ ()可以拦截对成员的访问,从而自定义属性的行为。getattr ()只有 在访问不存在的成员时才会被调用,getattribute ()拦截所有(包括不存在的成员)的获取操作。在__getattribute ()中不要 使用“return self. dict_ [name]"来返回结果,因为在访问self. dict_ 时同样会被__ getattribute__ ()拦 截,从而造成无限递归形成死循环。
__getattr__ (self, nane) #获取属性,比_ getattribute ()优先调用
__getattribute __( self, name) #获取属性
__setattr__ (self, name, value) #设置属性
__delattr__ (self, name)#删除属性
[例 9]自 定义属性示例 。
class CustomAttribute(object):
def __init__(self):
pass
def __getattribute__(self, name):
return str.upper(object.__getattribute__(self, name))
def __setattr__(self, name, value):
object.__setattr__(self, name, str.strip(value))
#测试
o = CustomAttribute()
o.firstname=' mary '
print(o.firstname)
程序运行结果如下。
MARY
方法是与类相关的函数,类方法的定义与普通的函数一致。在一般情况下,类方法的第一般为self, 这种方法称为对象实例方法。对象实例方法对类的某个给定的实例进行操作,可以通过self显式地访问该实例。对象实例方法的声明格式如下。
def 方法名(self,[形参列表1]):
函数体
对象实例方法的调用格式如下。
对象,方法名([实参列表])
值得注意的是,虽然类方法的第一个参数为self, 但调用时用户不需要也也不能给该参数传值。事实上,Python自动把对象实例传递给该参数。
例如,假设声明了一个类MyClass和类方法my _func(self,pl,p2),则:
objl = MyClass() #创建MyClass的对象实例obj1
obj1.my_ func (p1, p2) #调用对象obj1的方法
调用对象obj1的方法obj1. my_ func(pl. p2),Python自动转换为MyClass. my fume(obj1,pl,p2),即自动把对象实例obj1传值给self参数。
注意: Python中的self等价于C++中的self指针和Java.C#中的this 关键字。虽然没有限制第一个参数名必须为self,但建议读者遵循惯例,这样便于阅读和理解,且集成开发环境(IDE)也会提供相应的支持。
[例 10] 定义类Person4,创建其对象,并调用对象函数。
class Person4: #定义类Person4
def say_hi(self, name): #定义方法say_hi
self.name = name #把参数name赋值给self.name,即成员变量name(域)
print('您好, 我叫', self.name)
p4 = Person4() #创建对象
p4.say_hi('Alice') #调用对象的方法
程序运行结果如下。
您好,我叫Alice
Python也允许声明与类的对象实例无关的方法,称之为静态方法。静态方法不对特定实例进行操作,在静态方法中访向对象实例会导致错误。静态方法通过装饰器@ staticmethod来定义,其声明格式如下。
@ staticmethod
def静态方法名( [形参列表]) :
函数体
静态方法一般通过类名来访问,也可以通过对象实例来调用。其调用格式如下。
类名.静态方法名([实参列表])
[例 11] 摄氏温度与华氏温度之间的相互转换。
class TemperatureConverter:
@staticmethod
def c2f(t_c): #摄氏温度到华氏温度的转换
t_c = float(t_c)
t_f = (t_c * 9/5) + 32
return t_f
@staticmethod
def f2c(t_f): #华氏温度到摄氏温度的转换
t_f = float(t_f)
t_c = (t_f - 32) * 5 /9
return t_c
#测试代码
print("1. 从摄氏温度到华氏温度.")
print("2. 从华氏温度到摄氏温度.")
choice = int(input("请选择转换方向:"))
if choice == 1:
t_c = float(input("请输入摄氏温度: "))
t_f = TemperatureConverter.c2f(t_c)
print("华氏温度为: {0:.2f}".format(t_f))
elif choice == 2:
t_f = float(input("请输入华氏温度: "))
t_c = TemperatureConverter.f2c(t_f)
print("摄氏温度为: {0:.2f}".format(t_c))
else:
print("无此选项,只能选择1或2!")
Python也允许声明属于类本身的方法,即类方法。类方法不对特定实例进行操作,在类方法中访问对象实例属性会导致错误。
类方法通过装饰器@classmethod来定义,第一个形式参数必须为类对象本身,通常为cls.类方法的声明格式如下。
@classmethod
def类方法名(cls, [形参列表]) :
函数体
类方法一般通过类名来访问,也可以通过对 象实例来调用。其调用格式如下。
类名.类方法名([实参列表])
值得注意的是,虽然类方法的第一个参数为cls,但是调用时用户不需要也不能给该参数传值。事实上,Python自动把类对象传递给该参数。类对象与类的实例对象不同,在Python中类本身也是对象。在调用子类继承父类的类方法时传人的cls是子类对象,而非父类对象。
[例 12]类方 法示例 。
class Foo:
classname = "Foo"
def __init__(self, name):
self.name = name
def f1(self): #实例方法
print(self.name)
@staticmethod
def f2(): #静态方法
print("static")
@classmethod
def f3(cls): #类方法
print(cls.classname)
#测试代码
f = Foo("李")
f.f1()
Foo.f2()
Foo.f3()
在Python类体中可以定义特殊的方法,例如__ new__ (方法和_ init_ ()方法。
new ()方法是一 个类方法,在创建对象时调用,返回当前对象的一个实例,般无须重载该方法。
init()方法即构造函数(构造方法),用于执行类的实例的初始化工作。在创建完对象后调用,初始化当前对象的实例,无返回值。
[例 13] _ init_ ()方法示 例 。
class Person5: #定义类Person5
def __init__(self, name): #__init__方法
self.name = name #把参数name赋值给self.name,即成员变量name(域)
def say_hi(self): #定义类Person的方法say_hi
print('您好, 我叫', self.name)
p5 = Person5('Helen') #创建对象
p5.say_hi() #调用对象的方法
程序运行结果如下。
您好,我叫Helen
[例 14] 定义类Point,表示平面坐标点。
class Point:
def __init__(self, x = 0, y = 0): #构造函数
self.x = x
self.y = y
p1 = Point() #创建对象
print("p1({0},{1})".format(p1.x, p1.y))
p1 = Point(5, 5) #创建对象
print("p1({0},{1})".format(p1.x, p1.y))
程序运行结果如下。
p1(0,0)
p1(5,5)
在Python类体中可以定义一个特殊的方法__del__()方法。
del()方法即析构两数(析构方法),用于实现销毁类的实例所需的操作,如释放对象占用的非托管资源(例如打开的文件、网络连接等)。在默认情况下,当对象不再被使用时__del__ ()方法运行。 由于Python解释器实现自动垃圾回收,所以无法保证这个方法究竟在什么时候运行。
通过del语句可以强制销毁一个对象实例,从而保证调用对象实例的__del__ ()方法 。
[例 15]_del _()方法示例 。
class Person3:
count = 0 #定义类域count,表示计数
def __init__(self, name,age): #构造函数
self.name = name #把参数name赋值给self.name,即成员变量name(域)
self.age = age #把参数age赋值给self.age,即成员变量age(域)
Person3.count += 1 #创建一个实例时,计数加1
def __del__(self): #析构函数
Person3.count -= 1 #销毁一个实例时,计数减1
def say_hi(self): #定义类Person3的方法hi
print('您好, 我叫', self.name)
def get_count(): #定义类Person3的方法get_count
print('总计数为:', Person3.count)
print('总计数为:',Person3.count) #类名访问
p31 = Person3('张三',25) #创建对象
p31.say_hi() #调用对象的方法
Person3.get_count() #通过类名访问
p32 = Person3('李四',28) #创建对象
p32.say_hi() #调用对象的方法
Person3.get_count() #通过类名访问
del p31 #删除对象p31
Person3.get_count() #通过类名访问
del p32 #删除对象p32
Person3.get_count() #通过类名访问
程序运行结果如下。
总计数为: 0
您好,我叫张三
总计数为: 1
您好,我叫李四
总计数为: 2
总计数为: 1
总计数为: 0
与私有属性类似,Python约定以两个下画线开头,但不以两个下画线结束的方法是私有的(private)其他公共的(public)以双下面线开始和结束的方法是Pyhon 专有的特殊方法。注意不能直接访问私有方法但可以在其他方法中访问,
[例 16]私有方法示例 。
class Book: #定义类Book
def __init__(self, name, author, price):
self.name = name #把参数name赋值给self.name,即成员变量name(域)
self.author = author#把参数author赋值给self.author,即成员变量author(域)
self.price = price #把参数price赋值给self.price,即成员变量price(域)
def __check_name(self): #定义私有方法,判断name是否为空
if self.name == '' : return False
else: return True
def get_name(self): #定义类Book的方法get_name
if self.__check_name():print(self.name,self.author) #调用私有方法
else:print('No value')
b = Book('Python程序设计教程','江红',2.0) #创建对象
b.get_name() #调用对象的方法
#b.__check_name() #直接调用私有方法,非法
在其他程序设计语言中方法可以重载,即可以定义多个重名的方法,只要保证方法签名是唯一的即可。方法签名包括3个部分,即方法名、参数数量和参数类型。
Python本身是动态语言,方法的参数没有声明类型(在调用传值时确定参数的类型),参数的数量由可选参数和可变参数来控制。故Python对象方法不需要重载,定义一个方法即可实现多种调用,从而实现相当于其他程序设计语言的重载功能。
[例 17]方法 重载示例 1。
class Person21: #定义类Person21
def say_hi(self, name=None): #定义类方法say_hi
self.name = name #把参数name赋值给self.name,即成员变量name(域)
if name==None: print('您好! ')
else: print('您好, 我叫', self.name)
p21 = Person21() #创建对象
p21.say_hi() #调用对象的方法,无参数
p21.say_hi('威尔逊') #调用对象的方法,带参数程序运行结果如下。
程序运行结果如下。
您好!
您好,我叫威尔逊
在Python类体中定义多个重名的方法虽然不会报错,但只有最后一个方法有效,所以建议不要定义重名的方法。
[例 18]方法 重载示例2 。
class Person22: #定义类Person22
def say_hi(self, name): #定义类方法say_hi,带两个参数
print('您好, 我叫', self.name)
def say_hi(self, name, age): #定义类方法say_hi,带三个参数
print('hi, {0}, 年龄:{1}'.format(name,age))
p22 = Person22() #创建对象
p22.say_hi('Lisa', 22) #调用对象的方法
#p22.say_hi('Bob') #TypeError: say_hi() missing 1 required positional argument: 'age'
Pyhon支持多重继承,即一个派生类可以继承多个基类。派生类的声明格式如下。
class 派生类名(基类1, [基类2, …]):
类体
其中,派生类名后为所有基类的名称元组。如果在类定义中没有指定基类,则默认其基类为object。object是所有对象的根基类定义了公用方法的默认实现,如__new __().例如:
class Foo: pass
等同于:
class Foo(object); pass
在声明派生类时,必须在其构造函数中调用基类的构造函数。其调用格式如下。
基类名__init __(self, 参数列表)
[例 19] 创建基类Person,它包含两个数据成员name和age;创建派生类Student,它包含一个数据成员stu_id。
class Person: #基类
def __init__(self, name, age): #构造函数
self.name = name #姓名
self.age = age #年龄
def say_hi(self): #定义基类方法say_hi
print('您好, 我叫{0}, {1}岁'.format(self.name,self.age))
class Student(Person): #派生类
def __init__(self, name, age, stu_id): #构造函数
Person.__init__(self, name, age) #调用基类构造函数
self.stu_id = stu_id #学号
def say_hi(self): #定义派生类方法say_hi
Person.say_hi(self) #调用基类方法say_hi
print('我是学生, 我的学号为:', self.stu_id)
p1 = Person('张王一', 33) #创建对象
p1.say_hi()
s1 = Student('李姚二', 20, '2018101001') #创建对象
s1.say_hi()
程序运行结果如下。
您好,我叫张王一,33岁
您好,我叫李姚二,20岁
我是学生,我的学号为: 2018101001
多个类的继承可以形 成层次关系,通过类的方法mro()或类的属性__ mro__可以输出其继承的层次关系。
[例 20]查看类的继承关 系示例。
class A: pass
通过继承,派生类继承基类中除构造方法之外的所有成员。如果在派生类中重新定义从基类继承的方法,则派生类中定义的方法覆盖从基类中继承的方法。
[例 21]类成 员的继承和重写示例 。
class Dimension: #定义类Dimensions
def __init__(self, x, y): #构造函数
self.x = x #x坐标
self.y = y #y坐标
def area(self): #基类的方法area()
pass
class Circle(Dimension): #定义类Circle(圆)
def __init__(self, r): #构造函数
Dimension.__init__(self, r, 0)
def area(self): #覆盖基类的方法area()
return 3.14 * self.x * self.x #计算圆面积
class Rectangle(Dimension): #定义类Rectangle(矩形)
def __init__(self, w, h): #构造函数
Dimension.__init__(self, w, h)
def area(self): #覆盖基类的方法area()
return self.x * self.y #计算矩形面积
d1 = Circle(2.0) #创建对象:圆
d2 = Rectangle(2.0, 4.0) #创建对象:矩形
print(d1.area(), d2.area()) #计算并打印圆和矩形面积
程序运行结果如下。
12.568. 0
在该例中,派生类Circle和Rectangle继承了基类的成员变量x和y,重写了继承的方法area()。
在Python对象中包含许多以双下画线开始和结束的方法,称之为特殊方法。特殊方法通常在针对对象的某种操作时自动调用。
例如,在创建对象实例时(p1= Person( ‘张三’,23))自动调用其__init__ ()方法;在解释执行a [例 22对象的特殊方法示 例 。
class Person:
def __init__(self, name, age): #特殊方法(构造函数)
self.name = name
self.age = age
def __str__(self): #特殊方法,输出成员变量
return '{0}, {1}'.format(self.name,self.age)
#测试代码
p1 = Person('张三', 23)
print(p1)
程序运行结果如下。
张三,23
Python的运算符实际上是通过调用对象的特殊方法实现的。例如:
x=12;y=23
x+ y #等价于调用x._ _add__ (y). 输出:35
x.__add__ (y) #输出:35
Python运算符与对应的特殊方法如表9-3所示。
在Python类体中,通过重写各运算符对应的特殊方法即可实现运算符的重载。
[例 23]运算符重 载示例 。
class MyList: #定义类MyList
def __init__(self, *args): #构造函数
self.__mylist = [] #初始化私有属性,空列表
for arg in args:
self.__mylist.append(arg)
def __add__(self, n): #重载运算符"+",每个元素增加n
for i in range(0, len(self.__mylist)):
self.__mylist[i] += n
def __sub__(self, n): #重载运算符"-",每个元素减少n
for i in range(0, len(self.__mylist)):
self.__mylist[i] -= n
def __mul__(self, n): #重载运算符"*",每个元素乘以n
for i in range(0, len(self.__mylist)):
self.__mylist[i] *= n
def __truediv__(self, n): #重载运算符"/",每个元素除以n
for i in range(0, len(self.__mylist)):
self.__mylist[i] /= n
def __len__(self): #对应于内置函数len(),返回列表长度
return(len(self.__mylist))
def __repr__(self): #对应于内置函数str(),显示列表
str1 = ''
for i in range(0, len(self.__mylist)):
str1 += str(self.__mylist[i]) + ' '
return str1
#测试代码
m = MyList(1, 2, 3, 4, 5) #创建对象
m + 2; print(repr(m)) #每个元素加2
m - 1; print(repr(m)) #每个元素减1
m * 4; print(repr(m)) #每个元素乘4
m / 2; print(repr(m)) #每个元素除2
print(len(m)) #列表长度
支持大小比较的对象需要实现特殊方法,例如__eq ()、It()、le()、ge()、gt(),使用functools模块的total ordering 装饰器装饰类,则只需要实现__eq(),以及__lt__()、le()、ge()、__ gt__()中的任意一个。total ordering装饰器实现其他比较运算能简化代码量。
[例 24] total ordering 装饰器函数示例 。
import functools
@functools.total_ordering
class Student:
def __init__(self, firstname, lastname): #姓和名
self.firstname = firstname
self.lastname = lastname
def __eq__(self, other): #判断姓名是否一致
return ((self.lastname.lower(), self.firstname.lower()) ==
(other.lastname.lower(), other.firstname.lower()))
def __lt__(self, other): #self姓名
return ((self.lastname.lower(), self.firstname.lower()) <
(other.lastname.lower(), other.firstname.lower()))
#测试代码
if __name__ == '__main__':
s1 = Student('Mary','Clinton')
s2 = Student('Mary','Clinton')
s3 = Student('Charlie','Clinton')
print(s1==s2)
print(s1>s3)
在Python类体中可以定义一个特殊的方法__call__ ()方法。 定义了__call__()方法的对象称为可调用对象(callable),即该对象可以像函数样被调用。
[例 25]可调用对象示例 。
class GDistance: #类:自由落体距离
def __init__(self, g): #构造函数
self.g = g
def __call__(self, t): #自由落体下落距离
return (self.g*t**2)/2
#测试代码
if __name__ == '__main__':
e_gdist = GDistance(9.8) #地球上的重力加速度
for t in range(11): #自由落体0~10秒的下落距离
print(format(e_gdist(t), "0.2f"),end=' ') #调用可调用对象e_gdist
程序运行结果如下。
0.004.90 19.60 44.10 78.40 122.50 176. 40 240.10 313.60 396. 90 490. 00