python中定制类_Python3之定制类

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

Python中还有许多有特殊用途的函数,可以帮助我们定制类

__str__

先定义一个Student类,打印一个实例

>>> class Student(object):

... def __init__(self,name):

... self.name=name

...

>>> print(Student('Zhangsan'))

打印出<__main__.student object at>不好看,不直观

怎么才能打印的好看呢?只需要定义好__str__()方法,返回一个好看的字符串就可以了

>>> class Student(object):

... def __init__(self,name):

... self.name=name

... def __str__(self):

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

...

>>>

>>> print(Student('Zhangsan'))

Student object(name:Zhangsan)

这样打印出来的实例,不但好看 ,而且容易看出实例内部重要的数据

但是如果直接在终端敲变量而不用print,打印出来还是一样不好看

>>> Student('Zhangsan')

这是因为直接显示变量调用的不是__str__(),而是__repr__(),两者的区别是__str__()返回用户看到的字符串,而__repr__()返回程序开发者看到的字符串,也就是说,__repr__()是为调试服务的。

解决办法是再定义一个__repr__()。但是通常__str__()和__repr__()代码都是一样的,所以,有个偷懒的写法:

>>> class Student(object):

... def __init__(self,name):

... self.name=name

... def __str__(self):

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

... __repr__=__str__

...

>>>

>>> Student('Zhangsan')

Student object(name:Zhangsan)

__iter__

如果一个类想被用于for..in循环,类似list或tuple那样,就必须实现一个__iter__()方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()方法循环的拿到下一个值,直到遇到StopIteration错误退出循环

class Fib(object):

def __init__(self):

#初始化两个计数器

self.a,self.b=0,1

def __iter__(self):

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

return self

def __next__(self):

self.a,self.b=self.b,self.a+self.b

#设置循环退出条件

if self.a>10000:

raise StopIteration()

return self.a

for n in Fib():

print(n)

输出

>>> for n in Fib():

... print(n)

...

1

1

2

3

5

...

46368

75025

__grtitem__

Fib实例虽然能作用于for循环,看起来和list有点像,但是,把它当成list来使用还是不行,比如,取第5个元素

>>> Fib()[5]

Traceback (most recent call last):

File "", line 1, in

TypeError: 'Fib' object is not subscriptable

要表现的象list那样按照下标取出元素,需要实现__getitem__()方法

special_getitem.py

class Fib(object):

def __getitem__(self,n):

a,b=1,1

for x in range(n):

a,b=b,a+b

return a

现在,就可以按下标访问数列的任意一项了

>>> f[0]

1

>>> f[1]

1

>>> f[100]

573147844013817084101

f[0]相当于把n=0参数传递给方法__getitem__(0),因为n=0,for循环不执行,返回a=1,

f[1]相当于把n=1参数传递给方法__getitem__(1),因为n=1,for循环执行一次a=b=1,b=a+b=2 返回值为a=1

f[2]相当于把n=2参数传递给方法__geritem__(2),第一次循环,a=b=1,b=a+b=2 再次循环 a=b=2,b=a+b=1+2=3 两次循环结束返回值a=2

以此类推

但是list有个切片的方法对应Fib确报错

>>> f[1:5]

Traceback (most recent call last):

File "", line 1, in

File "", line 4, in __getitem__

TypeError: 'slice' object cannot be interpreted as an integer

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

special_getitem.py

class Fib(object):

def __getitem__(self,n):

#n是整数

if isinstance(n,int):

a,b=1,1

for x in range(n):

a,b=b,a+b

return a

#n是切片类似[0:2]

if isinstance(n,slice):

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:

L.append(a)

a,b=b,a+b

return L

f=Fib()

print(f[0])

print(f[1])

print(f[2])

print(f[3])

print(f[0:3])

如果传递的参数是整数则和上面的方法不变,一次返回一个整数

如果传递的参数是切片slice则从切片的start开始至stop结束返回一个列表

运行结果如下

1

1

2

3

[1, 1, 2]

如果传递参数是切片,执行过程分析f[0:1]切片取索引为0及第一个元素的列表

if判断[0:1]是slice

start=n.start 所以start=None

stop=n.stop stop=1

if判断如果start为None则把start置为0

定义初始两位数为1,1

定义空列表L=[]

执行循环 for x in range(1):

x=1执行第一次循环

if判断1>=1满足条件

执行append语句后L=[1]

接着执行往下语句a,b=b,a+b

a=b,b=a+b是同时执行执行完毕后

a=1 b=2

退出for循环返回列表L=[1]

如果传递的切片参数为[0:2]

if判断[0:2]是slice

start=n.start 所以start=None

stop=n.stop stop=2

if判断如果start为None则把start置为0

定义初始两位数为1,1

定义空列表L=[]

执行循环 for x in range(1):

x=0执行第一次循环

if判断0>=0满足条件

执行append语句后L.append(a) 及L.append(1) L=[1]

接着执行往下语句a,b=b,a+b

a=b,b=a+b是同时执行执行完毕后

a=1 b=2

for循环返回列表L=[1]

x=1执行第二次循环

if判断1>=1满足条件

执行append语句L.append(a)及L.append(1) L=[1,1]

执行a,b=b,a+b执行完毕后

a=2 b=3

因为stop=2执行两次以后退出循环 返回列表L=[1,1]

如果传递是切片参数是[0:3]

if判断[0:3]是slice

start=n.start 所以start=None

stop=n.stop stop=3

if判断如果start为None则把start置为0

定义初始两位数为1,1

定义空列表L=[]

执行循环 for x in range(1):

x=0执行第一次循环

if判断0>=0满足条件

执行append语句后L.append(a) 及L.append(1) L=[1]

接着执行往下语句a,b=b,a+b

a=b,b=a+b是同时执行执行完毕后

a=1 b=2

for循环返回列表L=[1]

x=1执行第二次循环

if判断1>=0满足条件

执行append语句L.append(a)及L.append(1) L=[1,1]

执行a,b=b,a+b执行完毕后

a=2 b=3

x=2执行第三次循环

if判断2>=0满足条件

执行append语句L.append(a)及L.append(2) L=[1,1,2]

因为stop=3执行三次次以后退出循环 返回列表L=[1,1,2]

以此类推

如果start不是 0

for循环会继续执行,但是还没有到start处因为不满足x>=start条件所以L.append(a)不会执行,不会往列表内追加元素

但是a,b=b,a+b会继续执行

但是以上还是有缺陷没有对step参数做处理

print(f[0:10:2])

[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

虽然加了参数:2步数还是1

也没有对负数进行处理,所以,要正确实现以下__getitem__()还有很多工作要做

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

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

__getattr__

正常情况下,当我们调用类的方法或属性时,如果不存在,就会报错。比如定了Student类

class Student(object):

def __init__(self):

self.name="Zhangsan"

调用存在的属性name没有问题,但是调用不存在的score属性就有问题了

>>> s.name

'Zhangsan'

>>> s.score

Traceback (most recent call last):

File "", line 1, in

AttributeError: 'Student' object has no attribute 'score'

错误信息很清楚地告诉我们,没有找到score这个attribute

要避免这个错误,除了可以加上一个score属性外,python还有另一个机制,那就是写一个__getattr__()方法,动态返回一个属性

special_getattr.py

class Student(object):

def __init__(self):

self.name = 'Michael'

def __getattr__(self, attr):

if attr=='score':

return 99

当调用不存在的属性时,比如score,Python解释器会试图调用__getattr__(self, 'score')来尝试获得属性,这样,我们就有机会返回score的值:

>>> s=Student()

>>> s.name

'Michael'

>>> s.score

99

返回函数也是完全可以的

class Student(object):

def __init__(self):

self.name = 'Michael'

def __getattr__(self, attr):

if attr=='score':

return 99

def __getattr__(self,attr):

if attr=='age':

return lambda:25

s=Student()

print(s.age())

调用方式改为s.age

注意,只有在没有找到属性的情况下,才调用__getattr__,已有的属性比如name,不会再__getattr__中查找

此外,注意到任意调用如s.abc都会返回None,这是因为我们定义的__getattr__默认返回就是None。要让class只响应特定的几个属性,我们就要按照约定,抛出AttributeError的错误:

>>> s=Student()

>>> s.name

'Michael'

>>> s.aba

>>> print(s.aba)

None

改成如下

def __getattr__(self,attr):

if attr=='age':

return lambda:25

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

__call__一个对象实例可以有自己是属性和方法,当我们调用实例方法时,我们用instance.method()来调用,能不能直接在实例本身上调用呢?

任何类,只需要定义一个__call__()方法,就可以直接对实例进行调用

special_call.py

class Student(object):

def __init__(self,name):

self.name=name

def __call__(self):

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

调用方法如下

>>> s=Student('Zhansan')

>>> s()

My name is Zhansan.

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

如果你把对象看成函数,那么函数本身其实也可以在运行期动态创建出来,因为类的实例都是运行期创建出来的,这么一来,我们就模糊了对象和函数的界限。

那么,怎么判断一个变量是对象还是函数呢?其实,更多的时候,我们需要判断一个对象是否能被调用,能被调用的对象就是一个Callable对象,比如函数和我们上面定义的带有__call__()的类实例:

>>> callable(Student('Zhangsan'))

True

>>> callable(max)

True

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

False

>>> callable(None)

False

>>> callable('str')

False

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值