面向对象编程Object Oriented Programming

面向对象编程

简称OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据操作数据的函数(方法)

面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行。

面向对象的三大特点:数据封装继承多态

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

在Python中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类(Class)的概念。

数据封装

面向对象编程的一个重要特点就是数据封装。

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

Student实例本身就拥有这些数据,要访问这些数据,就没有必要从外面的函数去访问,可以直接在类的内部定义访问数据**的函数,这样,就把“数据”给封装起来了。这些封装数据的函数是和类本身是关联起来的,我们称之为类的方法:

    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_grade(self):
            if self.score >= 90:
            return 'A'
            elif self.score >= 60:
                return 'B'
            else:
                return 'C'

    bart = Student('Bart Simpson', 59)
    bart.print_score()
    bart.get_grade()
    #Bart Simpson: 59
    #'C'

这样一来,我们从外部看Student类,就只需要知道,创建实例需要给出name和score,而如何打印,都是在Student类的内部定义的,这些数据和逻辑被“封装”起来了,调用很容易,但却不用知道内部实现的细节

访问限制

在Class内部,可以有属性和方法,而外部代码可以通过直接调用实例变量的方法来操作数据,这样,就隐藏了内部的复杂逻辑。
但是,从前面Student类的定义来看,外部代码还是可以自由地修改一个实例的name、score属性。如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问。

所以,我们把Student类改一改:

    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了,这样就确保了外部代码不能随意修改对象内部的状态,这样通过访问限制的保护,代码更加健壮。

有些时候,你会看到以一个下划线开头的实例变量名,比如_name,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。

双下划线开头的实例变量是不是一定不能从外部访问呢?其实也不是。不能直接访问__name是因为Python解释器对外把__name变量改成了_Student__name,所以,仍然可以通过_Student__name来访问__name变量

继承和多态

Python的继承和多态与Java一致,不赘述。

静态语言(如Java) vs 动态语言(如Python)

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

对于Python这样的动态语言来说,则不一定需要传入Animal类型。我们只需要保证传入的对象有一个run()方法就可以了。

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

获取对象信息

通过内置的一系列函数type()/isinstance()/dir()等方法,我们可以对任意一个Python对象进行剖析,拿到其内部的数据。要注意的是,只有在不知道对象信息的时候,我们才会去获取对象信息。

type()

首先,我们来判断对象类型,使用type()函数,基本类型都可以用type()判断:

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

如果一个变量指向函数或者类,也可以用type()判断。

type(abs)
<class 'builtin_function_or_method'>

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

isinstance()

要判断class的类型,可以使用isinstance()函数。isinstance()判断的是一个对象是否是该类型本身,或者位于该类型的父继承链上。

isinstance('a', str)

还可以判断一个变量是否是某些类型中的一种,比如下面的代码就可以判断是否是list或者tuple:

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

dir()

如果要获得一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list,比如,获得一个str对象的所有属性和方法。

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

类似__xxx__的属性和方法在Python中都是有特殊用途的,比如__len__方法返回长度。在Python中,如果你调用len()函数试图获取一个对象的长度,实际上,在len()函数内部,它自动去调用该对象的len()方法,所以,下面的代码是等价的:

len('ABC') == 'ABC'.__len__()

我们自己写的类,如果也想用len(myObj)的话,就自己写一个__len__()方法:

    class MyDog(object):
        def __len__(self):  
            return 100
    dog = MyDog()
    len(dog)

仅仅把属性和方法列出来是不够的,配合getattr()、setattr()以及hasattr(),我们可以直接操作一个对象的状态。

实例属性和类属性

由于Python是动态语言,根据类创建的实例可以任意绑定属性。给实例绑定属性的方法是通过实例变量,或者通过self变量:

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

    s = Student('Bob')
    s.score = 90        #这是实例属性

如果Student类本身需要绑定一个属性,可以直接在class中定义属性,这种属性是类属性,归Student类所有:

class Student(object):
    name = 'Student'

当我们定义了一个类属性后,这个属性虽然归类所有,但类的所有实例都可以访问到。

由于实例属性优先级比类属性高,千万不要把实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值