2.5.2Python-面向对象高级编程

总目录:https://www.jianshu.com/p/e406a9bc93a9

Python - 子目录:https://www.jianshu.com/p/50b432cb9460

面向对象还有很多高级特性,允许我们写出非常强大的功能。

我们会讨论多重继承、定制类、元类等概念。


__slots__

因为业务需求,我们会给一个类绑定很多变量或者方法,但是如果我们想要限制实例的属性呢?这就需要我们的  __slots__  .

举一个例子

class  Student(object):

    __slots__ = ('name','age')      # 定义允许绑定的属性名称

之后我们给他绑定属性

s = Student()

s.name ='张三'

s.age =20

s.score =90

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

由于'score'没有被放到__slots__中,所以不能绑定score属性,试图绑定score将得到AttributeError的错误。


同样有一点要注意的

class GraduateStudent(Student):

    pass

g = GraduateStudent()

g.score =9999

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

除非子类也定义__slots__,这样子类也会继承父类的__slots__。


@property

__slots__用来限制类的属性,那么@property是用来显示类的属性的值的。

在上面我们给score赋了一个9999的值,这显然不符合实际,我们首先可以用getXX和setXX的方法来拒绝直接将变量公布。

class Student(object):

    def get_score(self):

        return self._score

    def set_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

s = Student()

s.set_score(60)

print(s.get_score())

60

s.set_score(9999)

...

ValueError: score must between 0 ~ 100!

这样可以避免变量的公布,但是违背了Python的一个原则:优雅,所以Python有一个@property的内置装饰器 。

class Student(object):

    @property

    def score(self):

        return self._score


    @score.setter

    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


s = Student()

s.set_score(60)

print(s.get_score())

60

s.set_score(9999)

...

ValueError: score must between 0 ~ 100! 

@property将getter方法(这里指第一个score)变成了属性,同时生成了另一个装饰器@score.setter,这个装饰器负责把一个setter方法(这里指第二个score)变成属性赋值。

这里还有一个@property的特性,如果只有@property没有@XXX.setter,那么这个属性就变成了只读属性。


多重继承

我们在2.4.1Python-类基础中说过继承,我们在扩展一下这个问题。

我们将动物增加到四个

Dog - 狗;

Bat - 蝙蝠;

Parrot - 鹦鹉;

Ostrich - 鸵鸟。

同时增加哺乳动物和鸟类的类。

class Animal(object):

    pass


# 大类:

class Mammal(Animal):

    pass

class Bird(Animal):

    pass


# 各种动物:

class Dog(Mammal):

    pass

class Bat(Mammal):

    pass

class Parrot(Bird):

    pass

class Ostrich(Bird):

    pass

之后我们增加运动类

#动作

class Runnable(object):

    def run(self):

        print('Running...')


class Flyable(object):

    def fly(self):

        print('Flying...')

这样我们只需要让动物类多继承一个动作类即可。

class Animal(object):

    pass


# 大类:

class Mammal(Animal):

    pass

class Bird(Animal):

    pass


#动作

class Runnable(object):

    def run(self):

        print('Running...')

class Flyable(object):

    def fly(self):

        print('Flying...')


# 各种动物:

class Dog(Mammal,Runnable):

    pass

class Bat(Mammal,Flyable):

    pass

class Parrot(Bird,Flyable):

    pass

class Ostrich(Bird,Runnable):

    pass

在设计类的继承关系时,通常,主线都是单一继承下来的,例如,Ostrich继承自Bird。但是,如果需要“混入”额外的功能,通过多重继承就可以实现,比如,让Ostrich除了继承自Bird外,再同时继承Runnable。这种设计通常称之为MixIn。

参考:https://www.liaoxuefeng.com/wiki/1016959663602400/1017501628721248


定制类(魔法函数)

我们之前讲过,__init__,__slots__这样class自带的函数,其实class内部还自带很多这样的方法:

__str__

让类和对象一样能够进行友好的字符串打印。

class Student(object):

    def __init__(self, name):

        self.name = name

    def __str__(self):

        return "Student's name %s" %self.name


print(Student('Michael'))

Student's name Michael

这样就能简单的打印出来好看的字符串了。


__iter__

返回一个新的迭代器对象

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# 返回下一个值


for fin Fib():

    print(f)

1123581321345589


__getitem__

让类和序列一样能够进行切片操作

class Fib(object):

    def __getitem__(self, n):

        a, b =1, 1

        for xin range(n):

            a, b = b, a + b

        return a


print(Fib()[5])

8

__getitem__也生成了一个类似__iter__的迭代器对象,但是可以通过切片操作获取对应的元素。

__getattr__

让类和对象一样进行异常处理

class Student(object):

    def __init__(self):

        self.name ='Michael'


    def __getattr__(self, attr):

        if attr=='score':

            return '没有score属性'

s=Student()

print(s.score)

没有score属性

调用score属性,但是类中没有这个属性,如果不定义__getattr__,那么就会报错。


__call__

让实例可以直接调用

class Student(object):

    def __init__(self, name):

        self.name = name


    def __call__(self):

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


s=Student('张三')

s()

My name is 张三.


使用枚举类

如果我们需要大量常量,更好的方法是为这样的枚举类型定义一个class类型,然后,每个常量都是class的一个唯一实例。Python提供了Enum类来实现这个功能。


Enum()

from enum import Enum

Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))

for name, memberin Month.__members__.items():

    print(name, '=>', member, ',', member.value)

Enum()可以帮助我们完成枚举类型的构造,除此之外,还有枚举类:


@unique

from enumimport Enum, unique


@unique

class Weekday(Enum):

    Sun =0 # Sun的value被设定为0

    Mon =1

    Tue =2

    Wed =3

    Thu =4

    Fri =5

    Sat =6


day1 = Weekday.Mon

print(day1)

print(Weekday(2))

print(Weekday.Wed)

print(Weekday['Thu']) 

Weekday.Mon 

Weekday.Tue 

Weekday.Wed 

Weekday.Thu  


print(Weekday.Tue.value)

2


print(day1 == Weekday.Mon)

print(day1 == Weekday(1))

True

True

这是几种获取元素的方法。

参考:https://docs.python.org/3/reference/datamodel.html#special-method-names

你可能感兴趣的:(2.5.2Python-面向对象高级编程)