Python个人笔记-面向对象编程

面向对象编程——Object Oriented Programming,简称OOP。面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接受其他对象发过来的消息,并处理这些消息,计算机程序的执行就是在各个对象之间传递。

举例来说明面向过程和面向对象在程序流程上的不同之处:

假设要处理学生的成绩表,为表示学生的成绩,面向过程的程序可以用一个dict来表示:

std1 = {'name': 'Michael', 'score': 98}
std2 = {'name': 'Bob', 'score': 81}

打印学生成绩用函数实现:

def print_score(std):
    print('%s: %s' % (std['name'], std['score']))

如果采用面向对象的程序设计思想,首先思考的不是程序的执行过程,二是student这个数据类型应该被视为一个对象,它拥有name和score这两个属性(property):

class Student(object):

    def __init__(self, name, score):
        self.name = name
        self.score = score
    
    def print_score(self):
        print('%s: %s' % (self.name, self.score))

面向对象的程序的使用:

bart = Student('Bart Simpson', 59)
lisa = Student('Lisa Simpson', 87)
bart.print_score()
lisa.print_score()

上例中,Student便是一个类Class,bart和lisa便是一个实例Instance。

类与实例

类起到模板的作用,因此,可以在创建实例的时候把认为必须绑定的属性通过init方法强制填进去:

class Student(object):
    
    def __init__(self, name, score):
        self.name = name
        self.score = score

当然也可以自由地给一个实例变量绑定属性,如bart.gender = 'female'

访问限制

在之前的student类的定义来看,外部代码依然可以自由修改实例中的name和score:bart.score = 99。如果让内部属性不被外部访问,可以把属性的名称前加上两个下划线,这样它就变成一个私有变量,只有内部可以访问,外部不能访问:

class student(object):
    
    def __init__(self, name, score):
        self.__name = name
        self.__score = score
    
    def print_score(self):
        print('%s: %s' % (self.__name, self.__score))

通过如此的访问限制保护,代码更加健壮。如果外部想要获取name或score,可以通过增加get_name或get_score的方法。

需要注意,python中变量名类似xxx的,是特殊变量,可以直接访问。_xxx外部可以访问,但按照约定俗成的规定,一般将其默认视为私有变量。

student中的__name通过python解释器把这个变量改为了_student__name或类似的名称,也就是仍然可以从外部访问该变量。可以,但没必要。

注意一种错误写法

>>> bart = Student('Bart Simpson', 59)
>>> bart.get_name()
'Bart Simpson'
>>> bart.__name = 'New Name' # 设置__name变量!
>>> bart.__name
'New Name'

这样表面上是设置了name变量,但实际上这个__name和class内部的__name根本不是同一个变量,内部的__name已经被解释器自动更改为_Student__name,而外部代码给bart新增了一个__name变量。

继承和多态

在OOP程序设计中,定义一个class可以从现有的class继承,新的class称为子类(subclass),而被继承的class称为基类、父类或超类(Base class、 Super class)。

eg:

class Animal(object):
    def run(self):
        print('Animal is running...')

class Dog(Animal):
    pass

class Cat(Animal):
    pass

dog = Dog()
dog.run()

cat = Cat()
cat.run()

我们也可以对子类增加一些方法,当增加的方法与基类中的某方法相同时,子类的方法覆盖基类的方法,代码运行时总会调用子类的方法,这就是继承的另一个好处——多态:

class Dog(Animal):
    def run(self):
        print("Dog is running")

在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类,反之则不成立。

开闭原则:

  1. 对扩展开放:允许新增Animal子类;
  2. 对修改封闭:不需要修改依赖Animal类型的run_twice()等函数。
  • 静态语言 vs 动态语言

像java这种静态语言,如果需要传入Animal类型,则传入的对象必须是Animal类型或它的子类,否则将无法调用run()方法。

对于python这种动态语言,则不一定需要传入Animal类型,我们只需要保证传入的对象有一个run()方法即可。

获取对象信息

  • 判断对象类型,用type()函数

若要判断一个对象是否是函数,需要用types模块中定义的常量:

import types
def fn():
    pass

...

>>> type(fn)==types.FunctionType
True
>>> type(abs)==types.BuiltinFunctionType
True
>>> type(lambda x: x)==types.LambdaType
True
>>> type((x for x in range(10)))==types.GeneratorType
True
  • 也可以使用isinstance()函数:
>>> isinstance(dog, Animal)
True
  • 如果要获得一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list:
>>> dir('ABC')
['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']

类似xxx的属性或方法在python中有特殊用途,比如len方法返回长度,如果调用len()函数去获得一个函数的长度,实际上在len()函数内部自动去调用该对象的len方法,故下面的代码是等价的:

>>> len('ABC')
3
>>> 'ABC'.__len__()
3
  • 利用getattr(),setattr()和hasattr(),可以直接操作一个对象的状态:
>>> class MyObject(object):
...     def __init__(self):
...         self.x = 9
...     def power(self):
...         return self.x * self.x
...
>>> obj = MyObject()


>>> hasattr(obj, 'x') # 有属性'x'吗?
True
>>> obj.x
9
>>> hasattr(obj, 'y') # 有属性'y'吗?
False
>>> setattr(obj, 'y', 19) # 设置一个属性'y'
>>> hasattr(obj, 'y') # 有属性'y'吗?
True
>>> getattr(obj, 'y') # 获取属性'y'
19
>>> obj.y # 获取属性'y'
19


>>> getattr(obj, 'z') # 获取属性'z'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'MyObject' object has no attribute 'z'


>>> getattr(obj, 'z', 404) # 获取属性'z',如果不存在,返回默认值404
404


>>> hasattr(obj, 'power') # 有属性'power'吗?
True
>>> getattr(obj, 'power') # 获取属性'power'
<bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>>
>>> fn = getattr(obj, 'power') # 获取属性'power'并赋值到变量fn
>>> fn # fn指向obj.power
<bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>>
>>> fn() # 调用fn()与调用obj.power()是一样的
81

实例属性和类属性

由于python是动态语言,根据类创建的实例可以任意绑定属性:

class Student(object):
    def __init__(self, name):
        self.name = name
    
s = Student('Bob')
s.score = 90

也可以给类本身绑定一个属性,可以在class中定义属性,这种属性是类属性,归类所有:

class Student(object):
    name = 'Student'

定义类属性后,这个属性虽然归类所有,但类的所有实例都可以访问:

>>> s = Student() # 创建实例s
>>> print(s.name) # 打印name属性,因为实例并没有name属性,所以会继续查找class的name属性
Student
>>> print(Student.name) # 打印类的name属性
Student
>>> s.name = 'Michael' # 给实例绑定name属性
>>> print(s.name) # 由于实例属性优先级比类属性高,因此,它会屏蔽掉类的name属性
Michael
>>> print(Student.name) # 但是类属性并未消失,用Student.name仍然可以访问
Student
>>> del s.name # 如果删除实例的name属性
>>> print(s.name) # 再次调用s.name,由于实例的name属性没有找到,类的name属性就显示出来了
Student

相同名称的实例属性将屏蔽掉类属性,故尽量不要对实例属性和类属性使用相同的名字。

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

推荐阅读更多精彩内容