Python学习笔记__7.3章定制类

1、概览

看到类似__slots__这种形如__xxx__的变量或者函数名就要注意,这些在Python中是有特殊用途的。

__slots__我们已经知道怎么用了,__len__()方法我们也知道是为了能让class作用于len()函数。

除此之外,Python的class中还有许多这样有特殊用途的函数,可以帮助我们定制类。

1.1、__str__()  和 __repr__()

1、__str__()

修改print(instance) 显示的值

# 正常打印instance

>>> print (s)

<__main__.Student object at 0x00000005AD1EAAC8>

# 在类中加入__str__() 方法

...     def __str__(self):

...         return 'Student object (name: %s)' % self.name

# 再次打印

>>> print(s)

Student object (name: Bob)

2、__repr__()

修改 instance 显示的值

# 正常instance 显示

>>> s

<__main__.Student object at 0x00000005AD1EAAC8>

# 在类中加入__str__() 方法

...     def __repr__(self):

...         return 'Student object (name: %s)' % self.name

# 再次打印

>>>s

Student object (name: Bob)

  1. __str__()  和 __repr__() 的区别

__str__()返回用户看到的字符串,而__repr__()返回程序开发者看到的字符串

通常__str__()和__repr__()代码都是一样的。所以可以这样写

def __str__(self):

return 'Student object (name=%s)' % self.name

__repr__ = __str__

1.2、__iter__()

我们知道,只有 iterable对象可以进行 for…in… 循环。

如果想让一个类被用于for循环,就需要__iter__() 方法。该方法返回一个iterable 对象。然后,Python的for循环会不断调用该迭代对象的__next__()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。

  1. 以斐波那契数列为例,写一个Fib类,可以作用于for循环

class Fib(object):

def __init__(self):

self.a, self.b = 0, 1 # 初始化两个计数器a,b

def __iter__(self):

return self # 实例本身就是迭代对象,故返回自己

def __next__(self):

self.a, self.b = self.b, self.a + self.b # 计算下一个值

if self.a > 100000: # 退出循环的条件

raise StopIteration()

return self.a # 返回斐波那契数列

# 调用

>>> for n in Fib():   # for 循环打印 Fib() 实例

...     print(n)

1.3、__getitem__()

1、__getitem__()方法,可以实现 实例像list 一样的下标取值

# 类的编写

class Fib(object):

def __getitem__(self, n):

a, b = 1, 1

for x in range(n):

a, b = b, a + b

return a

# 实例的调用

>>> f = Fib()

>>> f[0]  # 0 就是传入的参数 n,返回的值是a的值

1

注:

虽然 f 可以像list一样,进行下标取值,但不能进行切片。

原因是__getitem__()传入的参数可能是一个int,也可能是一个切片对象slice,所以要做判断:

2、__getitem__()方法,实现 实例的切片

# 类的编写

class Fib(object):

def __getitem__(self, n):

if isinstance(n, int): # n是索引

a, b = 1, 1

for x in range(n):

a, b = b, a + b

return a

if isinstance(n, slice): # 判断n是否是切片

start = n.start # 切片开始

stop = n.stop # 切片结束

if start is None:

start = 0

a, b = 1, 1

L = []

for x in range(stop):

if x >= start:      # 判断a值 是否应该加入L

L.append(a)

a, b = b, a + b  # a的值一直再变。只是上面if 要判断是否保存a值

return L

# 调用

>>> f = Fib()

>>> f[0:5]

[1, 1, 2, 3, 5]

  • 总结

Fib(),现在可以下标取值 或s切片取值,但切片的处理没有步长和负数,所以,要正确实现一个__getitem__()还是有很多工作要做的。

此外,如果把对象看成dict,__getitem__()的参数也可能是一个可以作key的object,例如str。

与之对应的是__setitem__()方法,把对象视作list或dict来对集合赋值。最后,还有一个__delitem__()方法,用于删除某个元素。

1.3、关于切片对象slice的思考

slice一般是跟在list后面的。所以不能通过判断切片对list的操作,如L[0:5] 这样的表达式,来判断slice的数据类型。

在Python中,有一个slice对象,它的类型就是 slice。所以猜想:

上面的代码中的f[0:5],n==[0:5]。__getitem__(),会把[0:5],解释成 slice(0,5,none)。从而判断传入的参数是 slice。

n是slice的实例,有start,stop属性。所以通过 start = n.start ,stop = n.stop,来获取 slice 开始和结束的范围

1.4、__getattr__()

正常情况下,当我们调用类的方法或属性时,如果不存在,就会报错。那么,我们可以通过__getattr__()方法,动态返回一个属性。

__getattr__() 方法返回属性的前提是,在__getattr__()的函数体里,这个属性符合你设置的条件

1、动态返回属性

class Student(object):

def __init__(self):

self.name = 'Michael'

def __getattr__(self, attr):

if attr=='score':

return 99

# 我们只定义了name属性,如果尝试获取score属性,就会交由__getattr__() 方法处理

>>> s.score

99

# 如果请求的属性不符合__getattr__()方法的判断条件呢

>>> s.gender    # 无显示。正常会报错

>>> print(s.gender)  # 显示None,正常会报错

None

2、动态返回函数

class Student(object):

def __getattr__(self, attr):

if attr=='age':

return lambda: 25

# 调用

>>> s.age()

25

3、抛出错误

使用__getattr__()方法后,如果 某属性 class里没定义,__getattr__()里也没有,是不会抛出错误的。针对这些属性,如果要正常抛出错误,需要在__getattr__() 方法里定义

class Student(object):

def __getattr__(self, attr):

if attr=='age':

return lambda: 25

raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr)

1.5、__call__()

我们调用实例方法时,我们用instance.method()来调用。而定义一个__call__()方法,就可以直接对实例进行调用

class Student(object):

def __init__(self, name):

self.name = name

def __call__(self):

print('My name is %s.' % self.name)

# 调用

>>> s()  # self参数不要传入

My name is Michael.

__call__()还可以定义参数。对实例进行直接调用就好比对一个函数进行调用一样,所以你完全可以把对象看成函数,把函数看成对象,因为这两者之间本来就没啥根本的区别。

callable()函数,可以判断一个对象是否是“可调用”对象。

>>> callable(max)

True

>>> callable([1, 2, 3])

False

2、例题

1、利用完全动态的__getattr__,写出一个链式调用。完全动态的生成URL

class Chain(object):

def __init__(self,path=""): # 初始化实例,Chain().path 为空

self._path=path

def __getattr__(self,path): # 使用类没有定义的属性,就调用

return Chain("%s/%s"%(self._path,path))

def __call__(self,path):  # 直接对实例进行调用,将实例当作类似函数一样调用

return Chain("%s/%s"%(self._path,path))

def __str__(self):  # 实例显示的值

return self._path

__repr__=__str__

# 调用

print(Chain().a.b.user("ChenTian").c.d)

/a/b/user/ChenTian/c/d

调用解析:

  1. 创建了一个实例Chain()。
  2. Chain().a,类没有a属性,调用__getattr__() 方法,将 实例名和属性名传进去,返回一个Chain(/a)实例
  3. Chain(/a).b,操作同上,返回一个Chain(/a/b)实例
  4. Chain(/a/b).user("ChenTian"),先会执行getattr返回Chain实例,Chain(/a/b/user("ChenTian"))

然后由于有__call__方法,可以直接对实例调用。此时就会调用__call__方法。传入的path="ChenTian"。

然后返回Chain(/a/b/user/ChenTian)

  1. Chain(/a/b/user/ChenTian).c.d 操作同第2步

原文地址:http://blog.51cto.com/12758568/2116785

时间: 2024-07-30 18:51:24

Python学习笔记__7.3章定制类的相关文章

Python学习笔记__7.6章 使用原类

# 这是学习廖雪峰老师python教程的学习笔记 1.概览 动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的. 1.1.type() class的定义是运行时动态创建的,而创建class的方法就是使用type()函数. 通过type()函数创建的类和直接写class是完全一样的,因为Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type()函数创建出class. type()函数既可以返回一个对象的类型,又可以创建出新

Python学习笔记__7.5章 使用枚举类

# 这是学习廖雪峰老师python教程的学习笔记 1.概览 当我们需要定义有规律的常量时,比如月份.我们可以用Enum类来实现这个功能 1.1.直接使用Enum类 from enum import Enum Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec')) >>> type(Month) <class 'enum.En

Python学习笔记__7.3章 多重继承

# 这是学习廖雪峰老师python教程的学习笔记 一个子类可以有多个父类.这就叫多重继承.通过多重继承,一个子类就可以同时获得多个父类的所有功能. 1.1.Mixin 在设计类的继承关系时,通常,主线都是单一继承下来的. 通过多重继承,而给某个类添加额外功能.这种设计我们称为Mixin 定义 Mammal 类 class Mammal(Object): pass 定义 Runable 类 class Runnable(object): def run(self): print('Running.

Python学习笔记__7.2章 使用@property

# 这是学习廖雪峰老师python教程的学习笔记 1.概览 @property 可以让把[方法]当做[属性]调用 # 方法源码 class Student(object): def get__score(self): return self.__score def set__score(self, value): if not isinstance(value, int): raise ValueError('score must be an integer!') if value < 0 or

Python学习笔记__8.3章 单元测试

# 这是学习廖雪峰老师python教程的学习笔记 1.概览 单元测试是用来对一个模块.一个函数或者一个类来进行正确性检验的测试工作. 比如对函数abs(),我们可以编写出以下几个测试用例: 输入正数,比如1.1.2.0.99,期待返回值与输入相同: 输入负数,比如-1.-1.2.-0.99,期待返回值与输入相反: 输入0,期待返回0: 输入非数值类型,比如None.[].{},期待抛出TypeError. 把上面的测试用例放到一个测试模块里,就是一个完整的单元测试. 单元测试的意义: 方便的检测

Python学习笔记__8.4章 文档测试

# 这是学习廖雪峰老师python教程的学习笔记 1.概览 在文档中编写规范的注释代码.则Python内置的"文档测试"(doctest)模块可以直接提取注释中的代码并执行测试. 1.1.以abs()函数为例: #abs.py def abs(n): ''' # 两个为一对,换行输入 Function to get absolute value of number.  # 简单的介绍 Example: >>> abs(1)   # 测试 1 >>>

Python学习笔记__9.2章 StringIO 和 BytesIO

# 这是学习廖雪峰老师python教程的学习笔记 很多时候,数据读写不一定是文件,也可以在内存中读写. 1.StringIO StringIO顾名思义就是在内存中读写str. 1.1.写入StringIO 要把str写入StringIO,我们需要先创建一个StringIO,然后,像文件一样写入即可. >>> from io import StringIO   #  导入StringIO类 >>> f = StringIO()     # 创建一个实例,赋给f对象 >

Python学习笔记__10.4章 进程VS线程

# 这是学习廖雪峰老师python教程的学习笔记 1.概览 我们介绍了多进程和多线程,这是实现多任务最常用的两种方式.现在,我们来讨论一下这两种方式的优缺点 要实现多任务,通常我们会设计Master-Worker模式,Master负责分配任务,Worker负责执行任务,因此,多任务环境下,通常是一个Master,多个Worker. 如果用多进程实现Master-Worker,主进程就是Master,其他进程就是Worker. 如果用多线程实现Master-Worker,主线程就是Master,其

Python学习笔记__10.1章 多进程

# 这是学习廖雪峰老师python教程的学习笔记 1.概览 Unix/Linux操作系统提供了一个fork()系统调用.fork()调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回. 子进程永远返回0,而父进程返回子进程的ID.这样做的理由是,一个父进程可以fork出很多子进程,所以,父进程要记下每个子进程的ID,而子进程只需要调用getppid()就可以拿到父进程的ID. 1.1.创建子进程 Python的os模块封装了常见