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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355

推荐阅读更多精彩内容

  • 面向对象编程 OOP,是一种程序设计思想。OOP把对象作为程序的基本单元, 一个对象包含了数据和操作数据的函数。 ...
    公子小白123阅读 324评论 0 0
  • 写在前面的话 代码中的# > 表示的是输出结果 输入 使用input()函数 用法 注意input函数输出的均是字...
    FlyingLittlePG阅读 2,764评论 0 8
  • 私有化 .属性和方法访问权限私有的:在类的外部不可以被使用,,也不可以被继承保护的:在类的外部不可以被使用,也可以...
    ChiAo1fei阅读 245评论 0 0
  • 面向过程的编程思维是:按照处理流程,每一步需要做什么?用哪些函数可以解决?严格按照流程来把事情完成就ok了。这个在...
    redLion阅读 1,025评论 0 2
  • 高阶函数:将函数作为参数 sortted()它还可以接收一个key函数来实现自定义的排序,reversec参数可反...
    royal_47a2阅读 689评论 0 0