Python 面向对象编程

类和对象

定义类

Python支持面向对象编程,下面是一个例子。我们可以看到,在Python中声明类和其他语言差不多。不过实际上差别还是挺大的。

首先,Python没有严格意义上的构造函数,只有一个__init__(self,XXX)函数,该函数和构造函数的功能差不多,用来初始化对象的状态。之后创建对象的时候,直接使用类名和参数列表来创建,这样会调用初始化函数来创建对象。

特别要提一点,所有的Python类的实例函数的第一个参数必须是self,这个参数类似于Java的this关键字,指代当前对象。如果我们调用类上的方法a.f(),那么a这个实例就会传递给self参数。

下面介绍一下Python的实例字段。实例字段使用self.XXX来定义。Python不能像Java那样静态的声明字段。如果有需要,直接使用self.加字段名即可,这也是动态语言的一个特点。

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return f'Person(name:{self.name},age:{self.age})'


person=Person('yitian',24)
print(person)

上面这个例子还定义了一个__str__(self)函数,这个函数和Java的toString()函数类似,当输出的时候就会自动调用这个函数将对象转换为可读的字符串形式。Python中还有很多__XXX__形式的惯例函数,肩负着不同的职责。

共享字段和私有字段

首先需要说明,Python中没有publicprivate这样的字段访问修饰符,也就是说只要你想,你可以调用对象上的任意字段和方法。这里说的都只是一种编码的契约,我们在编写Python类的时候也要遵循这些契约,才能写出合格的代码来。

前面已经说了,实例字段使用self.来访问。如果在类中编写没有self的变量,那么这些变量就是类变量,可以在该类的所有对象之间共享。这个概念类似Java的静态字段。下面的population就是一个共享字段的例子。

class Person:
    population = 0

    def __init__(self, name, age):
        self.name = name
        self.age = age
        Person.population += 1

    def __str__(self):
        return f'Person(name:{self.name},age:{self.age})'


yitian = Person('yitian', 24)
zhang3 = Person('zhang3', 25)
print(yitian)
print(f'population:{Person.population}')

最后来说说私有字段。私有字段惯例上需要添加下划线_前缀。虽然这些“私有变量”也可以在类外边访问,但是我们千万不要这么做。私有字段作为类的内部实现,随时可能存在变化的可能,不应该向外部暴露。我们的代码中也不应该依赖其他类库的私有变量。

结构体

有时候我们可能需要结构体或者数据类这一概念,也就是将相关的变量封装到一个类中。在Python中可以定义一个空类,然后创建对象,并动态赋值。

print('--------------结构体--------------')


class StudentInfo:
    pass


info = StudentInfo()
info.name = 'yitian'
info.age = 24
info.gender = 'male'

print(f'info({info.name},{info.age},{info.gender})')

继承

单继承

支持定义类的语言一般也都支持继承,不然要这么个功能有什么用。如果要继承某个或某些类,在类定义上使用括号指定要继承的基类。如果需要访问基类的成员,使用基类名加点访问符.来访问。

class Student(Person):
    def __init__(self, id, name, age):
        Person.__init__(self, name, age)
        self.id = id

    def __str__(self):
        return f'Student(id:{self.id},name:{self.name},age:{self.age})'

    def introduce_myself(self):
        print(f"I'm {self.name}, I'm {self.age} years old student.")


xiaoming = Student(1, 'xiaoming', 14)
print(xiaoming)
xiaoming.introduce_myself()

继承层次

按照C++的概念,Python类的所有函数都是虚的,也就是说在子类中重写的所有函数,都会父类的同名函数。如果需要调用父类的版本,需要使用父类名.XXX的方式来访问。例如,如果我们要访问xiaoming的父类自我介绍。就需要使用下面的语句。

# 调用父类版本
Person.introduce_myself(xiaoming)

Python提供了两个内置函数isinstanceissubclass来帮我们判断类的继承关系。用法很简单,下面的结果依次是:真真真假。

print('--------------继承关系--------------')

print(f'xiaoming is Student:{isinstance(xiaoming,Student)}')
print(f'xiaoming is Person:{isinstance(xiaoming,Person)}')
print(f'Student is Person:{issubclass(Student,Person)}')
print(f'Person is Student:{issubclass(Person,Student)}')

多重继承

最后再来说说多重继承。多重继承的类签名类似下面这样。当我们访问子类的成员时,Python会先查找子类中存不存在该成员。如果不存在的话在查找父类,如果父类不存在就查找父类的父类,直到查到头为止。如果到这时候还没查找到就会抛出异常。

对于多重继承的话,这个过程可以简单的看成从左到右的、深度优先的查找过程:如果子类中不存在该成员,就先从Base1开始查找,如果Base1和它的所有父类都没有,再从Base2开始查找,以此类推。当然实际情况略微复杂一点,因为Python会检查类继承层次是否存在相同的父类,并确保相同的父类只访问一次。

class DerivedClassName(Base1, Base2, Base3):

迭代器和生成器

迭代器

在很多编程语言中都有迭代器的概念,迭代器可以在for-loop循环中使用。一般情况下迭代器会有next()hasNext()等类似的方法,确定什么时候应该停止迭代,什么时候返回元素。

在Python中需要使用__next__(self)函数来执行迭代,如果到了末尾则需要抛出StopIteration异常。如果编写了__next__(self)函数,我们就可以让__iter__(self):函数返回自身。这样一个迭代器就写好了,我们可以在for循环等地方使用了。

print('--------------迭代器--------------')


class IterableObj:
    def __init__(self, data):
        self.data = data
        self.index = -1

    def __iter__(self):
        return self

    def __next__(self):
        if self.index == len(self.data) - 1:
            raise StopIteration
        self.index += 1
        return self.data[self.index]


obj1 = IterableObj([1, 2, 3])
for i in obj1:
    print(i, end=' ')
print()

Python还包含了两个内置函数iter()next()用于创建迭代器和执行迭代。下面是使用列表迭代的例子。

list1 = [1, 2, 3]
iter1 = iter(list1)
e1 = next(iter1)
e2 = next(iter1)
e3 = next(iter1)

print('List:', e1, e2, e3)

生成器

迭代器虽然使用比较简单,但还是挺麻烦的。我们可以使用生成器更简单的创建迭代器。生成器其实就是一个函数,不过这个函数比较特殊,它不使用return返回结果,而是使用yield返回一系列值。当我们在循环中或者使用next()函数调用生成器的时候,每次调用生成器都会使用yield返回一个值。

print('--------------生成器--------------')


def even_generator(data):
    index = 0
    while index < len(data):
        if data[index] % 2 == 0:
            yield data[index]
        index += 1


integer_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

print(f'even_generator:{[i for i in even_generator(integer_list)]}')

从这个例子我们可以看到,生成器确实比迭代器更方便。

生成器表达式

生成器表达式其实和列表解析表达式差不多,只不过列表解析表达式使用方括号,而生成器表达式使用小括号。另外,生成器表达式返回的是一个生成器,而列表解析表达式返回的是列表。除此之外,它们在迭代的时候结果完全相同。

不过,由于生成器不是一次性生成所有值,所以当迭代的序列非常大的时候,最好使用生成器表达式而不是列表解析表达式。

print('--------------生成器表达式--------------')

odd_generator = (i for i in range(1, 11) if i % 2 != 0)

odd_list = [i for i in range(1, 11) if i % 2 != 0]

print(f'generator type:{type(odd_generator)}')
print(f'list type:{type(odd_list)}')

结果如下。

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

推荐阅读更多精彩内容

  • 面向对象编程 python中一切皆为对象,所谓对象:人是一个对象,电脑是一个对象 我们通过描述属性(特征)和行为来...
    b485c88ab697阅读 558评论 0 0
  • 类与实例 类与实例相互关联着:类是对象的定义,而实例是“真正的实物”,它存放了类中所定义的对象的具体信息。 下面的...
    邪恶的Sheldon阅读 327评论 0 0
  • 类相关知识 对象相关知识 类属性增删改查 实例属性增删改查 对象与实例属性 静态属性 类方法 静态方法 组合 继承...
    Techml阅读 400评论 0 0
  • 面向对象编程 面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思...
    蝴蝶兰玫瑰阅读 595评论 0 1
  • 面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想。OOP把对...
    勤快的树懒阅读 870评论 1 0