python学习笔记-魔术方法,让自定义类更像内置类型
Python的魔术方法是Python中那些预定义的像XXX类型的函数。
使用Python的魔术方法的最大优势在于python提供了简单的方法让对象可以表现得像内置类型一样。
__str__函数
str函数用于处理打印实例本身的时候的输出内容。如果没有覆写该函数,则默认输出一个对象名称和内存地址。
例如:
>>> class Student(object): ... def __init__(self,name): ... self._name = name ... >>> print Student()
输出:<main.Student object at 0x0000000002A929E8>.
那么我们如何让输出的结果可读性更高一点呢?我们可以覆写str函数。例如
>>> class Student(object): ... def __init__(self, name): ... self._name = name ... def __str__(self): ... return "I'm a student, named %s" % self._name ... >>> print Student("Charlie")
输出结果就是:I’m a student, named Charlie.
我们将str()函数作用于该对象的时候,其实是调用了该对象的str函数。
_repr_ 函数
repr也是将对象序列化,但是repr更多的是给python编译器看的。str更多的是可读性(readable)。
我们将repr()函数作用于摸某一个对象的时候,调用的其实就是该函数的repr函数。
与repr()成对的是eval()函数。eval()函数是将序列化后的对象重新转为对象。前提是该对象实现了repr函数。
上面这一段话基于自己的理解,不知道对错。
>>> item = [1,2,3] >>> repr(item) '[1, 2, 3]' >>> other_item = eval(repr(item)) >>> other_item[1] 2
__iter__函数
我们经常对list或者tuple使用for…in…来迭代。那是list继承自Iterable。Iterable实现了iter函数。
要想将一个自定义的对象变成一个可迭代的对象,那么必须要实现两个方法:iter和next.
iter函数返回一个对象。迭代的时候则会不断地调用next函数拿到下一个值,直到捕获到StopIteration停止。
廖雪峰老师教程里写的是next方法,不知道为啥。
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 i in Fib(): print i
__getitem__函数
上面通过实现iter函数实现对象的迭代。
那么如何实现对象按下标取出元素呢。
这是通过实现对象的getitem方法。
我们来举一个??子。我们新建了一个类MyList,我们要办它实现普通list的一些功能,比如(1)根据下标获取值;(2)正数顺序单步长切片 (3)任意步长切片
class MyList(object): def __init__(self, *args): self.numbers = args def __getitem__(self, item): return self.numbers[item] my_list = MyList(1, 2, 3, 4, 6, 5, 3) print my_list[2]
当然,上面实现了根据下标获取值。但是这还不够。我们还需要实现切片功能。例如my_list[1:3].
我们对对象进行切片操作的时候,调用的气势也是getitem函数。此时,该函数获取到的并不是int对象,而是slice对象。
例如下面的代码
class MyList(object): def __init__(self, *args): self.numbers = args def __getitem__(self, item): if isinstance(item, int): return self.numbers[item] elif isinstance(item, slice): # 写习惯了其他语言,差点忘记了三元运算符的格式了,吼吼吼。 # 下面句三元运算符的意思是,若为空,则为切片从0开始。 start = item.start if item.start is not None else 0 # 下面句三元运算符的意思是,若为空,则为切片到最末端结束。 stop = item.stop if item.stop is not None else len(self.numbers) return self.numbers[start:stop] my_list = MyList(1, 2, 3, 4, 6, 5, 3) print my_list[2:5]
上面的代码终于实现了切片功能,但是还没考虑负数呢。那么我们加一把劲再来改一下。代码如下:
class MyList(object): def __init__(self, *args): self.numbers = args def __getitem__(self, item): if isinstance(item, int): return self.numbers[item] elif isinstance(item, slice): start = item.start if item.start is not None else 0 stop = item.stop if item.stop is not None else len(self.numbers) length = len(self.numbers) start = length + start + 1 if start < 0 else start stop = length + stop + 1 if stop < 0 else stop return self.numbers[start:stop] my_list = MyList(1, 2, 3, 4, 6, 5, 3) print my_list[1:-1]
哇塞,写完了,棒棒棒
_getattar_
在调用某一个对象不存在的属性或者方法的时候,会抛出一个一个AttributeError错误。
但是如果我们实现了类中的魔术方法getattar,那么在调用不存在的属性或者方法的时候,就会调用该魔术方法。
class Apple(object): def __getattr__(self, item): if item == "attar1": return "print" if item == "method1": return lambda x: "hello %s" % x apple = Apple() print apple.attar1 print apple.method1
getattar函数一个重要的适用场景就是实现链式调用。例如我们在调用某一个api的时候:
GET users/articles/index
那么我们就希望我们的代码可以实现`Api.users.articles.index这么调用。
思考一下,要实现链式调用,最重要的就是每一个调用都是返回一个实例~~。
# coding=utf-8 class Api(object): def __init__(self, path=''): self._path = path def __getattr__(self, name): return Api("%s/%s" % (self._path, name)) # 定义一个Post方法来发送请求 def post(self): print self._path api = Api() api.user.articles.index.post()
廖雪峰在他的教程中给我们出了一个题目:
例如调用github的api:users/:user/repos一样,中间的user名需要动态替换。
我们希望能api.users("charlie").repos这么调用。那么代码该如何实现呢?这可能需要用到另一个方法call
_call_ 函数
一个对象既有属性,又有方法。我们在调用一个实例的方法的时候,我们可以使用instance.method()的形式调用。
其实也可以将实例本身看成一个函数用来调用,我们需要做的就是实现call函数本身。
class Apple(object): def __call__(self, *args, **kwargs): return args apple = Apple() print apple("yes", "no")
此时我们再来看一下上面提到的实现api.users("charlie").repos链式调用的方法。
# coding=utf-8 class Api(object): def __init__(self, path=''): self._path = path def __getattr__(self, name): return Api("%s/%s" % (self._path, name)) def __call__(self, args): self._path = "%s/%s" % (self._path, args) return Api(self._path) # 定义一个Post方法来发送请求 def post(self): print self._path api = Api() api.users("Charlie").index.post()
相关推荐
-
Flask项目集成富文本编辑器CKeditor python基础
2018-3-4
-
用 Python 处理 HTML 转义字符的5种方式 python基础
2018-2-10
-
Python计数模块Counter python基础
2019-8-29
-
ballGame python基础
2019-8-25
-
文件同步rsync python基础
2019-8-26
-
python☞自动发送邮件 python基础
2019-6-30
-
python – 协程 python基础
2019-3-22
-
朴素贝叶斯算法的简单机器学习应用 python基础
2019-8-29
-
Python 为什么不用分号作终止符? python基础
2020-6-11
-
PCA, SVD以及代码示例,机器学习中的数学(5)-强大的矩阵奇异值分解(SVD)及其应用 python基础
2019-5-16