2.5.2Python-面向对象高级编程

总目录:https://blog.csdn.net/qq_41106844/article/details/105553392

Python - 子目录:https://blog.csdn.net/qq_41106844/article/details/105553333

 

 

 

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

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

 

__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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寒 暄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值