(涉及内容:定制类)

__str__和__repr__

如果要把一个类的实例变成 str,就需要实现特殊方法__str__():

class Person(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
def __str__(self):
return '(Person: %s, %s)' % (self.name, self.gender)

现在,在交互式命令行下用 print 试试:

>>> p = Person('Bob', 'male')
>>> print p
(Person: Bob, male)

但是,如果直接敲变量 p

>>> p
<main.Person object at 0x10c941890>

似乎__str__() 不会被调用。

因为 Python 定义了__str__()__repr__()两种方法,__str__()用于显示给用户,而__repr__()用于显示给开发人员。

有一个偷懒的定义__repr__的方法:

class Person(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
def __str__(self):
return '(Person: %s, %s)' % (self.name, self.gender)
__repr__ = __str__

__cmp__

对 intstr 等内置数据类型排序时,Python的 sorted() 按照默认的比较函数 cmp 排序,但是,如果对一组 Student 类的实例排序时,就必须提供我们自己的特殊方法 __cmp__():

class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
def __str__(self):
return '(%s: %s)' % (self.name, self.score)
__repr__ = __str__ def __cmp__(self, s):
if self.name < s.name:
return -1
elif self.name > s.name:
return 1
else:
return 0

上述 Student 类实现了__cmp__()方法,__cmp__用实例自身self和传入的实例 进行比较,如果 self 应该排在前面,就返回 -1,如果 s 应该排在前面,就返回1,如果两者相当,返回 0。

Student类实现了按name进行排序:

>>> L = [Student('Tim', 99), Student('Bob', 88), Student('Alice', 77)]
>>> print sorted(L)
[(Alice: 77), (Bob: 88), (Tim: 99)]

注意: 如果list不仅仅包含 Student 类,则 __cmp__ 可能会报错:

L = [Student('Tim', 99), Student('Bob', 88), 100, 'Hello']
print sorted(L)

请思考如何解决。

__len__

如果一个类表现得像一个list,要获取有多少个元素,就得用 len() 函数。

要让 len() 函数工作正常,类必须提供一个特殊方法__len__(),它返回元素的个数。

例如,我们写一个 Students 类,把名字传进去:

class Students(object):
def __init__(self, *args):
self.names = args
def __len__(self):
return len(self.names)

只要正确实现了__len__()方法,就可以用len()函数返回Students实例的“长度”:

>>> ss = Students('Bob', 'Alice', 'Tim')
>>> print len(ss)
3

数学运算

Python 提供的基本数据类型 int、float 可以做整数和浮点的四则运算以及乘方等运算。

但是,四则运算不局限于int和float,还可以是有理数、矩阵等。

要表示有理数,可以用一个Rational类来表示:

class Rational(object):
def __init__(self, p, q):
self.p = p
self.q = q

p、q 都是整数,表示有理数 p/q。

如果要让Rational进行+运算,需要正确实现__add__:

class Rational(object):
def __init__(self, p, q):
self.p = p
self.q = q
def __add__(self, r):
return Rational(self.p * r.q + self.q * r.p, self.q * r.q)
def __str__(self):
return '%s/%s' % (self.p, self.q)
__repr__ = __str__

现在可以试试有理数加法:

>>> r1 = Rational(1, 3)
>>> r2 = Rational(1, 2)
>>> print r1 + r2
5/6

类型转换

Rational类实现了有理数运算,但是,如果要把结果转为 int  float 怎么办?

考察整数和浮点数的转换:

>>> int(12.34)
12
>>> float(12)
12.0

如果要把 Rational 转为 int,应该使用:

r = Rational(12, 5)
n = int(r)

要让int()函数正常工作,只需要实现特殊方法__int__():

class Rational(object):
def __init__(self, p, q):
self.p = p
self.q = q
def __int__(self):
return self.p // self.q

结果如下:

>>> print int(Rational(7, 2))
3
>>> print int(Rational(1, 3))
0

同理,要让float()函数正常工作,只需要实现特殊方法__float__()

@property

考察 Student 类:

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

当我们想要修改一个 Student 的 scroe 属性时,可以这么写:

s = Student('Bob', 59)
s.score = 60

但是也可以这么写:

s.score = 1000

显然,直接给属性赋值无法检查分数的有效性。

如果利用两个方法:

class Student(object):
def __init__(self, name, score):
self.name = name
self.__score = score
def get_score(self):
return self.__score
def set_score(self, score):
if score < 0 or score > 100:
raise ValueError('invalid score')
self.__score = score

这样一来,s.set_score(1000) 就会报错。

这种使用 get/set 方法来封装对一个属性的访问在许多面向对象编程的语言中都很常见。

但是写 s.get_score() 和 s.set_score() 没有直接写 s.score 来得直接。

有没有两全其美的方法?----有。

因为Python支持高阶函数,在函数式编程中我们介绍了装饰器函数,可以用装饰器函数把 get/set 方法“装饰”成属性调用:

class Student(object):
def __init__(self, name, score):
self.name = name
self.__score = score
@property
def score(self):
return self.__score
@score.setter
def score(self, score):
if score < 0 or score > 100:
raise ValueError('invalid score')
self.__score = score

注意: 第一个score(self)是get方法,用@property装饰,第二个score(self, score)是set方法,用@score.setter装饰,@score.setter是前一个@property装饰后的副产品。

现在,就可以像使用属性一样设置score了:

>>> s = Student('Bob', 59)
>>> s.score = 60
>>> print s.score
60
>>> s.score = 1000
Traceback (most recent call last):
...
ValueError: invalid score

说明对 score 赋值实际调用的是 set方法

__slots__

由于Python是动态语言,任何实例在运行期都可以动态地添加属性。

如果要限制添加的属性,例如,Student类只允许添加 name、genderscore 这3个属性,就可以利用Python的一个特殊的__slots__来实现。

顾名思义,__slots__是指一个类允许的属性列表:

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

现在,对实例进行操作:

>>> s = Student('Bob', 'male', 59)
>>> s.name = 'Tim' # OK
>>> s.score = 99 # OK
>>> s.grade = 'A'
Traceback (most recent call last):
...
AttributeError: 'Student' object has no attribute 'grade'

__slots__的目的是限制当前类所能拥有的属性,如果不需要添加任意动态的属性,使用__slots__也能节省内存。

__call__

在Python中,函数其实是一个对象:

>>> f = abs
>>> f.__name__
'abs'
>>> f(-123)
123

由于 f 可以被调用,所以,f 被称为可调用对象。

所有的函数都是可调用对象。

一个类实例也可以变成一个可调用对象,只需要实现一个特殊方法__call__()

我们把 Person 类变成一个可调用对象:

class Person(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender def __call__(self, friend):
print 'My name is %s...' % self.name
print 'My friend is %s...' % friend

现在可以对 Person 实例直接调用:

>>> p = Person('Bob', 'male')
>>> p('Tim')
My name is Bob...
My friend is Tim...

单看 p('Tim') 你无法确定 p 是一个函数还是一个类实例,所以,在Python中,函数也是对象,对象和函数的区别并不显著。

Python进阶 学习笔记(三)的更多相关文章

  1. 【原】Learning Spark (Python版) 学习笔记(三)----工作原理、调优与Spark SQL

    周末的任务是更新Learning Spark系列第三篇,以为自己写不完了,但为了改正拖延症,还是得完成给自己定的任务啊 = =.这三章主要讲Spark的运行过程(本地+集群),性能调优以及Spark ...

  2. python进阶学习笔记(一)

    python进阶部分要学习的内容: 学习目标: 1.函数式编程 1.1,什么是函数式编程 函数式编程是一种抽象计算的编程模式 不同语言的抽象层次不同: 函数式编程的特点: python支持的函数式编程 ...

  3. python进阶学习笔记(三)

    3.类的继承 3.1,python中什么是类的继承 答案是肯定的. 也就是说,如果一个实例是一个子类,那么它也是一个父类 总是从某各类继承,如果没有合适的类,就要从object类继承:super(). ...

  4. Python进阶 学习笔记(一)

    (笔记范围:第一章 课程介绍:第二章 函数式编程:第三章 模块) Python支持的函数式编程 不是纯函数式编程:允许有变量 支持高阶函数:函数也可以作为变量传入 支持闭包:有了闭包就能返回函数 有限 ...

  5. Python——追加学习笔记(三)

    错误与异常 AttributeError:尝试访问未知的对象属性 eg. >>> class myClass(object): ... pass ... >>> m ...

  6. python进阶学习笔记(四)--多线程thread

    在使用多线程之前,我们首页要理解什么是进程和线程. 什么是进程? 计算机程序只不过是磁盘中可执行的,二进制(或其它类型)的数据.它们只有在被读取到内存中,被操作系统调用的时候才开始它们的生命期.进程( ...

  7. python进阶学习(三)

    本节通过SQLite了解数据库操作 ------------------------- 数据库支持 使用简单的纯文本只能实现有退限的功能,所需要引入数据库,完成更强大的功能,本节使用的简单数据库SQL ...

  8. python进阶学习笔记(二)

    1.模块和包的概念 python的解决方案是把同名的模块放到不同的包中 1.1,导入模块 要使用一个模块,我们必须首先导入该模块.Python使用import语句导入一个模块.例如,导入系统自带的模块 ...

  9. Python进阶 学习笔记(二)

    (涉及内容:面向对象,类的继承) 定义类并创建实例 在Python中,类通过 class 关键字定义.以 Person 为例,定义一个Person类如下: class Person(object): ...

随机推荐

  1. Git 初始化项目、创建合并分支、回滚等常用方法总结

    就在刚才查看资料时候, 看见一句话, 写的特别好: 当我的才华撑不起我的梦想的时候, 应该安静下来学习 配上我最喜欢动漫的一个角色: 红莲 1. Git 初始化项目 1). 创建新的知识库 echo ...

  2. SpringMVC系列(四)使用 POJO 对象绑定请求参数值

    在实际开发中如果参数太多就不能使用@RequestParam去一个一个的映射了,需要定义一个实体参数对象(POJO)来映射请求参数.Spring MVC 会按请求参数名和 POJO 属性名进行自动匹配 ...

  3. e812. 强制弹出菜单为重组件

    By default, Swing popup menus used by JMenu and JPopupMenu are lightweight. If heavyweight component ...

  4. e868. 获取和设置本地外观

    By default, Swing uses a cross-platform look and feel called Metal. In most cases, it is more desira ...

  5. Python——eventlet.hubs

    Hub构成了 Eventlet 的事件循环,它分发 I/O 事件.调度 greenthread.Hub的存在使得协程被提升为 greenthreads. Eventlet 有多种hub的实现,所以在使 ...

  6. C# listview控件右击导出数据到txt文本

    private void 导出成功点击ToolStripMenuItem_Click(object sender, EventArgs e) { ) { MessageBox.Show("列 ...

  7. EayRadius 于 2013-7-19 进行体验度更新,增加用户体验度

    EasyRadius于2013-7-19进行更新,此次更新并没有更新通讯接口,通讯接口将统一更新,包括对其他路由的支持 下面我将主要更新的地方向大家描述一下 如果你有疑问或者建议,可以致电137799 ...

  8. TensorFlow-GPU:查看电脑显卡型号是否支持CUDN,以及相关软件下载与介绍

    1.显卡(GPU)是否支持CUDN https://developer.nvidia.com/cuda-gpus 2.了解基础知识 1)CUDA(Compute Unified Device Arch ...

  9. AsmTools

    前言 https://wiki.openjdk.java.net/display/CodeTools/asmtools 在OpenJDK里有一个AsmTools项目,用来生成正确的或者不正确的java ...

  10. Lynx

    http://www.unlinux.com/doc/soft/20051105/7402.html http://www.today-wx.com/linux/274.html 把 HTML 转成文 ...