草根学Python(九) 面向对象

来源:互联网 发布:淘宝卖家手册pdf 编辑:程序博客网 时间:2024/06/06 16:30

前言

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

公众号

目录

草根学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.__accountif __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._ageif __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.__accountclass UserInfo2(UserInfo):    passif __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._ageclass 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):    passclass User2(User1):    passclass User3(User2):    passif __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))

输出的结果如下:

TrueTrueTrueTrueTrueFalse

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

六、类的多态

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

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

>>> 1 + 23>>> '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。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 护士证注册的医院倒闭了怎么办 显示发货了但没物流信息怎么办 考科目三被别人举报了怎么办 科目一考了5没过怎么办 常州c1驾驶证满了12分怎么办 预约科目三成功后没交钱怎么办 预约成功后驾校不提交档案怎么办 c2刚满一年的驾照扣12分怎么办 怀孕6个月咳嗽很厉害怎么办 家门口有电线杆影响我建楼房怎么办 卡丢了不知道卡号怎么办 驾考网上预约用户被锁定了怎么办 人才中心拿出来的户口掉了怎么办 父母是南京集体户孩子没户口怎么办 二建挂靠中介单位不给证怎么办 小包工头遇到工人在工地摔伤怎么办 外地人买了城中村的房子改造怎么办 深圳社保怀孕了产检异地怎么办 成都公租房租满6年怎么办 二建审核资料如果照片丢失怎么办 身份证被冒用在外地办社保怎么办 蔷薇的嫩叶都被太阳晒死了怎么办 乐视手机进水了屏幕失灵怎么办 乐视手机进水了屏幕不显示怎么办 美团商家单量出现下滑怎么办 想开个小超市没经营过怎么办 华为7c手机wifi信号差怎么办 贞子从电视里爬出来怎么办 2个月的婴儿吓到怎么办 排卵日同房了没避孕怕怀孕怎么办 妻子因为我欺骗她要跟我离婚怎么办 老婆用苹果手机共享我的位置怎么办 孕妇餐后2小时血糖偏高怎么办 孕妇血糖餐后2小时数值高怎么办 股市退市的话股民的钱怎么办 美股股票退市了手里的股票怎么办 坐高铁安检时怕把包包弄坏了怎么办 很贵的包包高铁安检怎么办 如果过高铁安检东西被扣留怎么办 邻居家小孩把我家东西弄坏了怎么办 邻居早上6点放音乐扰民怎么办