美文网首页【Python】Python 运维程序员
草根学Python(九) 面向对象

草根学Python(九) 面向对象

作者: GitHubClub | 来源:发表于2017-07-31 00:33 被阅读249次

    前言

    这篇写的很纠结,不过还是写完了。弄了个很逊的公众号,如果对本文有兴趣,可以关注下公众号喔,会持续更新。

    公众号

    目录

    草根学Python(九)面向对象

    一、面向对象的概念

    Python 是一门面向对象的语言, 面向对象是一种抽象,抽象是指用分类的眼光去看世界的一种方法。 用 JAVA 的编程思想来说就是:万事万物皆对象。也就是说在面向对象中,把构成问题事务分解成各个对象。

    面向对象有三大特性,封装、继承和多态。

    1、面向对象的两个基本概念

    用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。

    • 对象

    通过类定义的数据结构实例

    2、面向对象的三大特性

    • 继承

    即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。

    例如:一个 Dog 类型的对象派生自 Animal 类,这是模拟"是一个(is-a)"关系(例图,Dog 是一个 Animal )。

    • 多态

    它是指对不同类型的变量进行相同的操作,它会根据对象(或类)类型的不同而表现出不同的行为。

    • 封装性

    “封装”就是将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体(即类);封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,一特定的访问权限来使用类的成员。

    二、类

    1、定义类

    类定义语法格式如下:

    class ClassName:
        <statement-1>
        .
        .
        .
        <statement-N>
    

    一个类也是由属性和方法组成的,有些时候我们定义类的时候需要设置类的属性,因此这就需要构造函

    类的构造函数如下:

    def __init__(self,[...):
    

    类定义了 init() 方法的话,类的实例化操作会自动调用 init() 方法。

    那么如构造函数相对应的是析构函数,理所当然,一个类创建的时候我们可以用过构造函数设置属性,那么当一个类销毁的时候,就会调用析构函数。

    析构函数语法如下:

    def __del__(self,[...):
    

    仔细观察的童鞋都会发现,类的方法与普通的函数有一个特别的区别,它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self。

    那么这个 self 代表什么呢?

    我们可以看下实例,通过实例来找出答案:

    #!/usr/bin/env python3
    # -*- coding: UTF-8 -*-
    
    class Test:
        def prt(self):
            print(self)
            print(self.__class__)
    
    t = Test()
    t.prt()
    

    观察输出的结果:

    Python self

    从执行结果可以很明显的看出,self 代表的是类的实例,输出的是当前对象的地址,而 self.__class__ 则指向类。

    当然 self 不是 python 关键字,也就是说我们把他换成其他的字符也是可以正常执行的。只不过我们习惯使用 self

    2、Python 定义类的历史遗留问题

    Python 在版本的迭代中,有一个关于类的历史遗留问题,就是新式类和旧式类的问题,具体先看以下的代码:

    #!/usr/bin/env python
    # -*- coding: UTF-8 -*-
    
    # 旧式类
    class OldClass:
        pass
    
    # 新式类
    class NewClass(object):
        pass
    
    

    可以看到,这里使用了两者中不同的方式定义类,可以看到最大的不同就是,新式类继承了object 类,在 Python2 中,我们定义类的时候最好定义新式类,当然在 Python3 中不存在这个问题了,因为 Python3 中所有类都是新式类。

    那么新式类和旧式类有什么区别呢?

    运行下下面的那段代码:

    #!/usr/bin/env python
    # -*- coding: UTF-8 -*-
    
    # 旧式类
    class OldClass:
        def __init__(self, account, name):
            self.account = account;
            self.name = name;
    
    
    # 新式类
    class NewClass(object):
        def __init__(self, account, name):
            self.account = account;
            self.name = name;
    
    
    if __name__ == '__main__':
        old_class = OldClass(111111, 'OldClass')
        print(old_class)
        print(type(old_class))
        print(dir(old_class))
        print('\n')
        new_class=NewClass(222222,'NewClass')
        print(new_class)
        print(type(new_class))
        print(dir(new_class))
    
    

    仔细观察输出的结果,对比一下,就能观察出来,注意喔,Pyhton3 中输出的结果是一模一样的,因为Python3 中没有新式类旧式类的问题。

    三、类的属性

    1、直接在类中定义属性

    定义类的属性,当然最简单最直接的就是在类中定义,例如:

    class UserInfo(object):
        name='两点水'
    

    2、在构造函数中定义属性

    故名思议,就是在构造对象的时候,对属性进行定义。

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

    3、属性的访问控制

    在 Java 中,有 public (公共)属性 和 private (私有)属性,这可以对属性进行访问控制。那么在 Python 中有没有属性的访问控制呢?

    一般情况下,我们会使用 __private_attrs 两个下划线开头,声明该属性为私有,不能在类地外部被使用或直接访问。在类内部的方法中使用时 self.__private_attrs

    为什么只能说一般情况下呢?因为实际上, Python 中是没有提供私有属性等功能的。但是 Python 对属性的访问控制是靠程序员自觉的。为什么这么说呢?看看下面的示例:

    Python 属性访问控制

    仔细看图片,为什么说双下划线不是真正的私有属性呢?我们看下下面的例子,用下面的例子来验证:

    
    #!/usr/bin/env python
    # -*- coding: UTF-8 -*-
    
    class UserInfo(object):
        def __init__(self, name, age, account):
            self.name = name
            self._age = age
            self.__account = account
    
        def get_account(self):
            return self.__account
    
    
    if __name__ == '__main__':
        userInfo = UserInfo('两点水', 23, 347073565);
        # 打印所有属性
        print(dir(userInfo))
        # 打印构造函数中的属性
        print(userInfo.__dict__)
        print(userInfo.get_account())
        # 用于验证双下划线是否是真正的私有属性
        print(userInfo._UserInfo__account)
    
    
    

    输出的结果如下图:

    Python 属性访问控制

    四、类的方法

    1、类专有的方法

    一个类创建的时候,就会包含一些方法,主要有以下方法:

    类的专有方法:

    方法 说明
    __init__ 构造函数,在生成对象时调用
    __del__ 析构函数,释放对象时使用
    __repr__ 打印,转换
    __setitem__ 按照索引赋值
    __getitem__ 按照索引获取值
    __len__ 获得长度
    __cmp__ 比较运算
    __call__ 函数调用
    __add__ 加运算
    __sub__ 减运算
    __mul__ 乘运算
    __div__ 除运算
    __mod__ 求余运算
    __pow__ 乘方

    当然有些时候我们需要获取类的相关信息,我们可以使用如下的方法:

    • type(obj):来获取对象的相应类型;
    • isinstance(obj, type):判断对象是否为指定的 type 类型的实例;
    • hasattr(obj, attr):判断对象是否具有指定属性/方法;
    • getattr(obj, attr[, default]) 获取属性/方法的值, 要是没有对应的属性则返回 default 值(前提是设置了 default),否则会抛出 AttributeError 异常;
    • setattr(obj, attr, value):设定该属性/方法的值,类似于 obj.attr=value;
    • dir(obj):可以获取相应对象的所有属性和方法名的列表:

    2、方法的访问控制

    其实我们也可以把方法看成是类的属性的,那么方法的访问控制也是跟属性是一样的,也是没有实质上的私有方法。一切都是靠程序员自觉遵守 Python 的编程规范。

    示例如下,具体规则也是跟属性一样的,

    #!/usr/bin/env python
    # -*- coding: UTF-8 -*-
    
    class User(object):
        def upgrade(self):
            pass
    
        def _buy_equipment(self):
            pass
    
        def __pk(self):
            pass
    
    

    3、方法的装饰器

    • @classmethod
      调用的时候直接使用类名类调用,而不是某个对象

    • @property
      可以像访问属性一样调用方法

    具体的使用看下实例:

    #!/usr/bin/env python
    # -*- coding: UTF-8 -*-
    
    class UserInfo(object):
        lv = 5
    
        def __init__(self, name, age, account):
            self.name = name
            self._age = age
            self.__account = account
    
        def get_account(self):
            return self.__account
    
        @classmethod
        def get_name(cls):
            return cls.lv
    
        @property
        def get_age(self):
            return self._age
    
    
    if __name__ == '__main__':
        userInfo = UserInfo('两点水', 23, 347073565);
        # 打印所有属性
        print(dir(userInfo))
        # 打印构造函数中的属性
        print(userInfo.__dict__)
        # 直接使用类名类调用,而不是某个对象
        print(UserInfo.lv)
        # 像访问属性一样调用方法(注意看get_age是没有括号的)
        print(userInfo.get_age)
    

    运行的结果:

    Python 方法的装饰器

    五、类的继承

    1、定义类的继承

    首先我们来看下类的继承的基本语法:

    class ClassName(BaseClassName):
        <statement-1>
        .
        .
        .
        <statement-N>
    

    在定义类的时候,可以在括号里写继承的类,一开始也提到过,如果不用继承类的时候,也要写继承 object 类,因为在 Python 中 object 类是一切类的父类。

    当然上面的是单继承,Python 也是支持多继承的,具体的语法如下:

    class ClassName(Base1,Base2,Base3):
        <statement-1>
        .
        .
        .
        <statement-N>
    

    多继承有一点需要注意的:若是父类中有相同的方法名,而在子类使用时未指定,python 在圆括号中父类的顺序,从左至右搜索 , 即方法在子类中未找到时,从左到右查找父类中是否包含方法。

    那么继承的子类可以干什么呢?

    继承的子类的好处:

    • 会继承父类的属性和方法
    • 可以自己定义,覆盖父类的属性和方法

    2、调用父类的方法

    一个类继承了父类后,可以直接调用父类的方法的,比如下面的例子,UserInfo2 继承自父类 UserInfo ,可以直接调用父类的 get_account 方法。

    #!/usr/bin/env python
    # -*- coding: UTF-8 -*-
    
    class UserInfo(object):
        lv = 5
    
        def __init__(self, name, age, account):
            self.name = name
            self._age = age
            self.__account = account
    
        def get_account(self):
            return self.__account
    
    
    class UserInfo2(UserInfo):
        pass
    
    
    if __name__ == '__main__':
        userInfo2 = UserInfo2('两点水', 23, 347073565);
        print(userInfo2.get_account())
    
    

    3、父类方法的重写

    当然,也可以重写父类的方法。

    示例:

    #!/usr/bin/env python3
    # -*- coding: UTF-8 -*-
    
    class UserInfo(object):
        lv = 5
    
        def __init__(self, name, age, account):
            self.name = name
            self._age = age
            self.__account = account
    
        def get_account(self):
            return self.__account
    
        @classmethod
        def get_name(cls):
            return cls.lv
    
        @property
        def get_age(self):
            return self._age
    
    
    class UserInfo2(UserInfo):
        def __init__(self, name, age, account, sex):
            super(UserInfo2, self).__init__(name, age, account)
            self.sex = sex;
    
    
    if __name__ == '__main__':
        userInfo2 = UserInfo2('两点水', 23, 347073565, '男');
        # 打印所有属性
        print(dir(userInfo2))
        # 打印构造函数中的属性
        print(userInfo2.__dict__)
        print(UserInfo2.get_name())
    
    

    最后打印的结果:

    Python 类的继承

    这里就是重写了父类的构造函数。

    3、子类的类型判断

    对于 class 的继承关系来说,有些时候我们需要判断 class 的类型,该怎么办呢?

    可以使用 isinstance() 函数,

    一个例子就能看懂 isinstance() 函数的用法了。

    #!/usr/bin/env python3
    # -*- coding: UTF-8 -*-
    
    class User1(object):
        pass
    
    
    class User2(User1):
        pass
    
    
    class User3(User2):
        pass
    
    
    if __name__ == '__main__':
        user1 = User1()
        user2 = User2()
        user3 = User3()
        # isinstance()就可以告诉我们,一个对象是否是某种类型
        print(isinstance(user3, User2))
        print(isinstance(user3, User1))
        print(isinstance(user3, User3))
        # 基本类型也可以用isinstance()判断
        print(isinstance('两点水', str))
        print(isinstance(347073565, int))
        print(isinstance(347073565, str))
    
    

    输出的结果如下:

    True
    True
    True
    True
    True
    False
    

    可以看到 isinstance() 不仅可以告诉我们,一个对象是否是某种类型,也可以用于基本类型的判断。

    六、类的多态

    多态的概念其实不难理解,它是指对不同类型的变量进行相同的操作,它会根据对象(或类)类型的不同而表现出不同的行为。

    事实上,我们经常用到多态的性质,比如:

    >>> 1 + 2
    3
    >>> 'a' + 'b'
    'ab'
    

    可以看到,我们对两个整数进行 + 操作,会返回它们的和,对两个字符进行相同的 + 操作,会返回拼接后的字符串。也就是说,不同类型的对象对同一消息会作出不同的响应。

    看下面的实例,来了解多态:

    #!/usr/bin/env python3
    # -*- coding: UTF-8 -*-
    
    class User(object):
        def __init__(self, name):
            self.name = name
    
        def printUser(self):
            print('Hello !' + self.name)
    
    
    class UserVip(User):
        def printUser(self):
            print('Hello ! 尊敬的Vip用户:' + self.name)
    
    
    class UserGeneral(User):
        def printUser(self):
            print('Hello ! 尊敬的用户:' + self.name)
    
    
    def printUserInfo(user):
        user.printUser()
    
    
    if __name__ == '__main__':
        userVip = UserVip('两点水')
        printUserInfo(userVip)
        userGeneral = UserGeneral('水水水')
        printUserInfo(userGeneral)
    
    

    输出的结果:

    Hello ! 尊敬的Vip用户:两点水
    Hello ! 尊敬的用户:水水水
    

    可以看到,userVip 和 userGeneral 是两个不同的对象,对它们调用 printUserInfo 方法,它们会自动调用实际类型的 printUser 方法,作出不同的响应。这就是多态的魅力。

    要注意喔,有了继承,才有了多态,也会有不同类的对象对同一消息会作出不同的相应。

    最后,本章的所有代码都可以在 https://github.com/TwoWater/Python 上面找到,文章的内容和源文件都放在上面。同步更新到 Gitbooks。

    相关文章

      网友评论

        本文标题:草根学Python(九) 面向对象

        本文链接:https://www.haomeiwen.com/subject/crhllxtx.html