< 笔记 > Python - 07 Python 面向对象编程(OOP)

07 Python 面向对象编程(OOP)

By Kevin Song

  • 07-01 类和实例
  • 07-02 访问限制
  • 07-03 继承和多态
  • 07-04 获取对象信息
  • 07-05 实例属性和类属性

面向过程 的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行。为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数通过切割成小块函数来降低系统的复杂度

面向对象 的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递

07-01 类(Class)和实例(Instance)

Python定义类(Class)

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))

Python创建对象并调用内部函数访问参数

kevin = Student('Kevin', 86)
kevin.print_score1()

数据封装

函数内部方法,除了第一个参数是self外,其他和普通函数一样。要调用一个方法,只需要在实例变量上直接调用,除了self不用传递,其他参数正常传入

class Student(object):
    ...

    def get_grade(self):
        if self.score >= 90:
            return 'A'
        elif self.score >= 60:
            return 'B'
        else:
            return 'C'
>>> kevin.get_grade()
'B'

外部函数访问参数

>>> def print_score(std):
...     print('%s: %s' % (std.name, std.score))
...
>>> print_score(kevin)
kevin: 86

07-02 访问限制

为了让内部name和score属性不被外部访问,需要通过两个下划线 “__” 私有化属性

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))
    def get_name(self):
        return self.__name
    def get_score(self):
        return self.__score
    def set_score(self, score):
        self.__score = score

私有变量(private),只有内部可以访问,外部不能访问

>>> kevin = Student('Kevin', 86)
>>> kevin.__name
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute '__name'

私有变量的好处:可以对参数做检查,避免传入无效的参数

def set_score(self, score):
        if 0 <= score <= 100:
            self.__score = score
        else:
            raise ValueError('bad score')
  • __xxx__
    • 特殊变量,可以直接访问
  • xxx
    • 公开变量,可以直接访问
  • _xxx
    • 私有变量,可以直接访问,但是按照规定,请不要随意访问
  • __xxx
    • 私有变量,不可以直接访问,可以通过_类名__xxx强制访问(不建议)

07-03 继承和多态

  • 子类Dog和Cat 继承 父类Animal
  • 子类Dog和Cat 重写 父类run方法
class Animal(object):
    def run(self):
        print('Animal is running...')

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

class Cat(Animal):
    def run(self):
        print('Cat is running...')
>>> dog = Dog()
>>> dog.run()
Dog is running...
>>> cat = Cat()
>>> cat.run()
Cat is running...
  • 多态: dog是Dog也是Animal

多态的优点:新增一个Animal的子类,不必对run_twice()做任何修改,就会自动调用实际类型的run()方法

def run_twice(animal):
    animal.run()
    animal.run()
>>> run_twice(Dog())
Dog is running...
Dog is running...
>>> run_twice(Cat())
Cat is running...
Cat is running...

“开闭”原则:

对扩展开放:允许新增Animal子类;

对修改封闭:不需要修改依赖Animal类型的run_twice()等函数。

静态语言 vs 动态语言

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

动态语言(Python):不一定需要传入Animal类型。我们只需要保证传入的对象有一个run()方法就行,因此继承不是必须的

比如Timer也可以传入run_twice()方法

class Timer(object):
    def run(self):
        print('Start...')

这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子

07-04 获取对象信息

  • type()
    • 返回对应Class类型
  • isinstance()
    • 返回布尔类型
  • dir()
    • 返回包含字符串的list

使用type()

判断对象类型

>>> type(123)
<class 'int'>
>>> type('str')
<class 'str'>
>>> type(None)
<type(None) 'NoneType'>

判断方法类型

>>> type(abs)
<class 'builtin_function_or_method'>

判断类类型

>>> type(a)
<class '__main__.Animal'>

type()函数返回的是对应的Class类型

>>> type(123)==type(456)
True
>>> type(123)==int
True
>>> type('abc')==type('123')
True
>>> type('abc')==str
True
>>> type('abc')==type(123)
False

使用isinstance()

>>> a = Animal()
>>> d = Dog()
>>> h = Husky()
>>> isinstance(h, Husky)
True
>>> isinstance(h, Animal)
True

判断一个变量是否是某些类型中的一种

>>> isinstance([1, 2, 3], (list, tuple))
True
>>> isinstance((1, 2, 3), (list, tuple))
True

使用dir()

>>> dir('ABC')
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

调用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

注意:
如果可以直接写

sum = obj.x + obj.y

就不要写

sum = getattr(obj, 'x') + getattr(obj, 'y')

原因

def readImage(fp):
    if hasattr(fp, 'read'):
        return readData(fp)
    return None

从文件流fp中读取图像,首先判断该fp对象是否存在read方法

  • 如果存在,则该对象是一个流
  • 如果不存在,则无法读取。

07-05 实例属性和类属性

给实例绑定属性:通过self变量

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

s = Student('Bob')
s.score = 90

给类绑定属性:直接在class中定义属性

class Student(object):
    name = 'Student'

测试

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

注意:

不要把实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当删除实例属性后,再使用相同的名称,访问到的将是类属性

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值