Python学习笔记之__slot__、@property、__str__、__iter__、__getitem、__getattr__、和__call__

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/kabuto_hui/article/details/87909146


注意】出现的 __slot____init____sstr__等,都是 双下划线

1. __slots__

Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性:

class Student(object):
    __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称

也就是说创建新的实例的时候,只能绑定限定的属性,否则就会出错:

s = Student()
s.name = 'Tom'
s.age = 22
s.score=100
---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-10-3ee075cc4fd4> in <module>()
      2 s.name = 'Tom'
      3 s.age = 22
----> 4 s.score=100


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

因为限定了属性,所以绑定属性score的时候就报错了。

2. @property

既能检查参数,又可以用类似属性这样简单的方式来访问类的变量。Python内置的property饰器就是负责把一个方法变成属性调用的:

class Student(object):
    @property
    def name(self):
        return self._name
    @name.setter
    def name(self, s):
        if isinstance(s, str):
            self._name = s
        else:
            raise ValueError('Name must be a string')

s = Student()
s.name='hello'
print(s.name)
hello

可以看到在函数定义前加上@property,就把一个函数变成了一个属性在使用,而且这个属性还能被检查。因为用@同名.setter就起到了设置的作用,在设置中就可以进行检查,那么@property就起到了一个getter的作用,用于获取一个属性的值。

特殊用法】只定义getter方法,不定义setter方法就是一个只读属性:

class Student(object):

    @property
    def birth(self):
        return self._birth

    @birth.setter
    def birth(self, value):
        self._birth = value

    @property
    def age(self):
        return 2015 - self._birth

3. __str__

直接打印类的时候,如print(Student('Tom')),输出的结果是<__main__.Student object at 0x109afb190>,就不太好看,于是只需要定义好_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 object (name: Michael)

如果用变量的方式,打出来还是不好看:

s = Student(‘Michael’)
<main.Student object at 0x109afb310>

扫描二维码关注公众号,回复: 5754894 查看本文章

这是因为直接显示变量调用的不是__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__

s = Student('Tom')
print(s)
Student object (name=Tom)

4. __iter__

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

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 > 100: # 退出循环的条件
            raise StopIteration()
        return self.a # 返回下一个值

print([n for n in Fib()])
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

5. __getitem__

Fib实例虽然能作用于for循环,看起来和list有点像,但是,把它当成list来使用还是不行,比如,取第5个元素:Fib()[5]就会报错。要表现得像list那样按照下标取出元素,需要实现__getitem__()方法:

class Fib(object):
    def __getitem__(self, n):
        a, b = 1, 1
        for x in range(n):
            a, b = b, a + b
        return a
print(Fib()[5])
8

6. __getattr__

python中有一个__getattr__()方法,可以动态返回一个属性。当调用不存在的属性时,比如score,Python解释器会试图调用__getattr__(self, 'score')来尝试获得属性,这样,我们就有机会返回score的值:

class Student(object):

    def __init__(self):
        self.name = 'Michael'

    def __getattr__(self, attr):
        if attr=='score':
            return 99

s = Student()
print('s.name=', s.name)
print('s.score=', s.score)
print('s.age=', s.age)
s.name= Michael
s.score= 99
s.age= None

注意】只有在没有找到属性的情况下,才调用__getattr__,已有的属性,比如name,不会在__getattr__中查找。此外,注意到任意调用如s.age都会返回None,这是因为我们定义的__getattr__默认返回就是None。要让class只响应特定的几个属性,我们就要按照约定,抛出AttributeError的错误:

class Student(object):

    def __init__(self):
        self.name = 'Michael'

    def __getattr__(self, attr):
        if attr=='score':
            return 99
        raise AttributeError('This object has no attribute named ',attr)

s = Student()
print('s.name=', s.name)
print('s.score=', s.score)
print('s.age=', s.age)
s.name= Michael
s.score= 99



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

AttributeError                            Traceback (most recent call last)

<ipython-input-27-4721b134eb99> in <module>()
     12 print('s.name=', s.name)
     13 print('s.score=', s.score)
---> 14 print('s.age=', s.age)


<ipython-input-27-4721b134eb99> in __getattr__(self, attr)
      7         if attr=='score':
      8             return 99
----> 9         raise AttributeError('This object has no attribute named ',attr)
     10 
     11 s = Student()


AttributeError: ('This object has no attribute named ', 'age')

7. __call__

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

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

    def __call__(self):
        print('My name is %s.' % self.name)

s = Student('Tom')
print(s())    # 直接调用
My name is Tom.
None

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

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

callable(Student('Tom'))
True

猜你喜欢

转载自blog.csdn.net/kabuto_hui/article/details/87909146