Python学习 (六 面向对象高级编程)

前言:现在开始使用Pycharm作为运行代码的环境,继续延伸第五部分的知识要点。

6.1 使用__slots__

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

class Student(object):
    __slots__ = ('name','score') #不是变量名,而是字符串?

s=Student()
s.name = 'Peter'
s.gender = 'male'  #无法赋值
Traceback (most recent call last):
  File "C:/Users/PGH/PycharmProjects/test/test-1.py", line 6, in <module>
    s.gender = 'male'
AttributeError: 'Student' object has no attribute 'gender'

__slots__定义的属性仅对当前类起作用,对继承的子类是不起作用的。

6.2 使用@property

Python内置的@property装饰器就是负责把一个方法变成属性调用的,也就是为了实现s.name = ''这样的输入。
需要注意的是使用__init__的时机是为了在创建实例的时候把必须绑定的属性填进去。

class Student(object):
    @property
    def score(self):  #没有@的话,调用应该是按方法的模式
        return self._score
    @score.setter     #这是`@property`自身创建的
    def score(self,value):
        if not isinstance(value,int):
            raise ValueError('score must be an integer!')
        if value < 0 or value >100:
            raise ValueError('score must between 0~100!')
        self._score = value

    @property
    def rank(self):  #定义只读属性,只定义getter方法,不定义setter方法就是一个只读属性
        if self._score < 60:
            return 'C'
        if self._score < 90:
            return  'B'
        else:
            return  'A'

s = Student()
s.score = 90
print s.rank
A

6.3 多重继承

多重继承指的是有好多干爹?继承所有父类的所有的功能,如下:

class Dog(Mammal,Runnable,Hastail):
    pass

Mixin

其实可以把 Mixin 看作多重继承的一种在特定场景下的应用,同时也是为了更好的看出继承的关系,知道哪些是额外混入的功能

class Dog(Mammal, RunnableMixin, CarnivorousMixin):
    pass

6.4 定制类

重申一下,对于Python中__xxx__这样的变量或者函数名,都是有特殊用途的,需要额外注意。下面再简单介绍几个。

__str__

#Windows PowerShell 环境
>>> class Student(object):
...     def __init__(self,name):
...         self.name = name
...
>>> Student('Peter')
<__main__.Student object at 0x02A380F0>
#不美观= =其实我觉得无所谓,只是没有反映出变量名
>>> class Student(object):
...     def __init__(self,name):
...         self.name = name
...     def __str__(self):
...         return 'Student object (name:%s)' % self.name
...
>>> print Student('Peter') #相当于调用了`__str__`方法
Student object (name:Peter)
>>> s = Student('Peter') #不用赋值也一样
>>> s # 调用的是`__repr__`方法,这是为调试服务的
<__main__.Student object at 0x02A382D0>
>>> class Student(object):
...     def __init__(self,name):
...         self.name = name
...     def __str__(self):
...         return 'Student object (name: %s)' % self.name
...     __repr__ = __str__ #偷懒= =
...
>>> Student('Peter')
Student object (name: Peter)

__iter__

适用于构建一个能被for...in...循环的类,类似于listtuple,它有一个大致的流程。__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): #关键是这一个计算的过程,这是真循环体,__iter__()只是引子
        self.a,self.b = self.b,self.a + self.b
        if self.a > 100:
            raise StopIteration();
        return self.a

for i  in Fib():
    print i
1
1
2
3
5
...
89

__getitem__

上述例子无法实现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

与之对应的是__setitem__()方法,把对象视作list或dict来对集合赋值。最后,还有一个__delitem__()方法,用于删除某个元素。要完整实现一个list的功能,那就得所有配合着来用了。

__getattr__

Python有一个机制,那就是写一个__getattr__()方法,动态返回一个属性,比如说你调用的那个不是固定值,那么Python解释器会试图调用__getattr__(self, 'xxx')来尝试获得属性,可以返回值,也可以返回函数。

class Student(object):

    def __getattr__(self, attr):
        if attr=='age':
            return lambda: 25
        raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr  #这是一个错误约定
  • 只有在没有找到属性的情况下,才调用
  • 调用不存在的就返回None
  • 可以针对完全动态的情况使用(不明觉厉)

__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 = Student('Peter')
s()
My name is Peter

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

6.5 使用元类

type()

动态语言静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的。
首先学习一下对于模块的调用,首先编译好一个test.py文件,里头一个类(Hello),然后编译器运行在该路径下,直接输入from test import Hello。示例如下:

class Hello(object):
    def hello(self,name = 'world'):
        print 'hello, %s' % name
>>> from test import Hello #动态创建出一个Hello的class对象
>>> h = Hello()
>>> h.hello()
hello, world

type()函数可以查看一个类型或变量的类型,Hello是一个class,它的类型就是type,而h是一个实例,它的类型就是class Hello
我们可以通过type()函数创建出Hello类,而无需通过class Hello(object)...的定义:

>>> def fn(self, name='world'): # 先定义函数
...     print('Hello, %s.' % name)
...
>>> Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class

要创建一个class对象,type()函数依次传入3个参数:

  • class的名称;
  • 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
  • class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。
    其实生成的class和用class定义是一样的,这里只是为了说明一下该函数的使用,自己还是怎么方便怎么来吧。。。

metaclass(元类)

作用:控制类的创建行为。
先定义metaclass,就可以创建类,最后创建实例。只不过我们一般省略了第一步。
按照默认习惯,metaclass的类名总是以Metaclass结尾。
介于这个太复杂,我还是留到以后再慢慢研究吧= =
对于实例属性和类属性提一下,直接在class中定义的是类属性,如:

class Student(object):
    name = 'Student'

实例属性必须通过实例来绑定,比如self.name = 'xxx',当然,属性并不一定需要最初在定义类中,然而在同时存在的时候,也就是明明相同时,优先调用的类属性,所以,千万不要把实例属性和类属性使用相同的名字

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值