定制类(魔法方法)

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

_str_

class Student(object):
    def __init__(self, name):
        self.name = name
print(Student('Michael'))
<__main__.Student object at 0x10d9a16a0>
打印一串 <__main__.Student object at 0x10d9a16a0>,怎么才能打印更好看呢?只需要重写__str__()方法,就可以返回指定的内容了
class Student(object):
    def __init__(self, name):
        self.name = name
    
    def __str__(self):
        return 'Student object (name: %s)' % self.name
print(Student('Michael'))
Student("cody")
Student object (name: Michael)





<__main__.Student at 0x10d9a1c88>
如果不用print 打印出的实例依然是 <__main__.Student at 0x10d9a1b38>
  • __str__()返回用户看到的字符串--> 需要通过print()调用显示
  • __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__

_iter_

如果一个类想被for... in ...循环,必须实现一个__iter__() 方法,该方法返回一个迭代对象,然后,Python的for循环不断调用该迭代对象的__next__()方法拿到循环的下一个值,直到遇StopIteration错误时退出循环。
斐波那契额数列 用于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 # 计算下一个值
        
        # 等价上面的语句
        # self.a = self.b
        # self.b = self.a + self.b
        if self.a > 100000: # 退出循环的条件
            raise StopIteration()
        return self.a
for n in Fib():
    print(n)
# TypeError: 'Fib' object does not support indexing
Fib()[5]
# Fib虽然可以用作for,但是不能当做list来使用,比如取索引值
1
1
2
3
5
8
...
10946
17711
28657
46368
75025



---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-12-ef3892c3fd8e> in <module>()
     15 for n in Fib():
     16     print(n)
---> 17 Fib()[5]


TypeError: 'Fib' object does not support indexing

_getitem_

如果要表现得跟list一样,可以通过索引取值,就需要实现__getitem__()方法
class Fib(object):
    def __getitem__(self, n = 1000):
        a, b = 1, 1
        for x in range(n):
            a, b = b, a + b
        return a
f = Fib()
# for i in f:
#     print(i)
f[0]
f[5]
8
list的神奇切片方法
list(range(100))[5:10]
[5, 6, 7, 8, 9]
但是该方法对于Fib报错,原因是__getitem__()传入的参数可能是一个int,也可能是一个切片对象slice,所以要做判断
class Fib(object):
    def __getitem__(self, n):
        if isinstance(n, int): # n是索引
            a, b = 1, 1
            for i 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 i in range(stop):
                if i >= start:
                    L.append(a)
                a, b = b, a + b
            return L
# Fib的切片
f = Fib()
f[0:5]
[1, 1, 2, 3, 5]

_getattr_

当调用类的方法或属性时,如果不存在,就会报错
class Student(object):
    def __init__(self):
        self.name = 'Michael'

s = Student()
print(s.name)
# AttributeError: 'Student' object has no attribute 'score'
print(s.score)
Michael



---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-16-09213cfcfc0a> in <module>()
      6 print(s.name)
      7 # AttributeError: 'Student' object has no attribute 'score'
----> 8 print(s.score)


AttributeError: 'Student' object has no attribute 'score'
如果要避免调用一个不存在的属性时不保存,python提供 __getattr__()方法,动态返回一个属性。
class Student(object):
    def __init__(self):
        self.name = 'Michael'
    
    def __getattr__(self, attr):
        if attr == 'score':
            return 99
s = Student()
print(s.name)
print(s.score)
Michael
99
返回函数也可以实现
class Student(object):
    def __getattr__(self, attr):
        if attr == 'age':
            return lambda: 25
s = Student()
# 改变调用方法
s.age()
25
需要注意的是,只有没有找到属性的情况下,才调用__getattr__,已有的属性不会再__getattr__里面找,这种情况下,如果调用s.abcdef __getattr__ 就返回None,所以为了其他的方法或属性 直接抛出异常AttributeError
class Student(object):
    def __getattr__(self, attr):
        if attr == "age":
            return lambda: 25
        raise AttributeError(f"Student object has no attribute {attr}")
s = Student()
print(s.age())
print(s.aaa)
print(s.abc())
25



---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-28-14a64c261ef9> in <module>()
      6 s = Student()
      7 print(s.age())
----> 8 print(s.aaa)
      9 print(s.abc())


<ipython-input-28-14a64c261ef9> in __getattr__(self, attr)
      3         if attr == "age":
      4             return lambda: 25
----> 5         raise AttributeError(f"Student object has no attribute {attr}")
      6 s = Student()
      7 print(s.age())


AttributeError: Student object has no attribute aaa
__getattr__()的应用场景

现在很多网站都搞REST API,比如新浪微博、豆瓣啥的,调用API的URL类似:

class Chain(object):
    def __init__(self, path=''):
        self._path = path
    
    def __getattr__(self, path):
        return Chain('%s/%s' % (self._path, path)) 
    
    def __str__(self):
        return self._path
    
    __repr__ = __str__
Chain().status.user.timeline.list
/status/user/timeline/list

_call_

一个实例可以有自己的属性和方法,通过instance.method()来调用. call实现直接在实例本身上调用,任何类只需要定义一个__call__()方法就可以直接对实例进行调用
class Student(object):
    def __init__(self, name):
        self.name = name
    
    def __call__(self):
        print('My name is %s' % self.name)
s = Student('Michael')
s() # self参数不传

My name is Michael
函数和方法没有本质上的区别
怎么判断一个变量是对象还是函数呢? 判断一个对象是否能被调用,能被调用的对象其实就是一个Callable对象,比如定义了带__call__()的类实例
print(callable(Student('cody')))
print(callable(max))
print(callable([1, 2, 3]))
print(callable(None))
print(callable('str'))
True
True
False
False
False
通过callable()函数,判断一个对象是否是“可调用”对象。
posted on 2018-10-31 13:46 想喝猪肝汤 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/codycc/p/9882559.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值