Python入门基础第十五课--面向对象

    1.前言

    事实上,Python被称为面向对象的语言,和C++、Java语言是一样的,创建自己的对象,特别是类型或者被称为类的对象是Python里面最重要最重要的概念。前面我们介绍的都是Python内建的对象:数字、字符串、列表、元组、字典。从这节开始,我们会介绍如何创建对象,以及多态、封装、方法、特性、超类以及继承的相关知识。我们开始吧!注意类似<span style="color:#cc0000;">这样的HTML标签语言是设置颜色的时候显示出来的,本身并不在程序里面。自己动手练习的时候要将它们去掉。

    2.多态

    多态从字面意思来理解就是多种多样。多态意味着就算不知道变量所引用的对象类型是什么,我们也可以对它们进行操作,而它会根据对象或者类的类型而表现出不同的行为。也就是可以对不同类的对象使用同样的操作。

    我们来做一个小小的测试:

标准库random里面有一个choice函数,可以从序列中随机选取元素。

>>> from random import choice
>>> x=choice(['Hello,World!',[1,2,3,'e','e',9]])
>>> x.count('e')
2

在这里,我们检测什么并不重要,重要的是你知道x带有一个count方法可以返回你所检测类型在整个被检测类型中出现的次数。当然,如果有人和你一样创建了带有count方法的对象,那也无关紧要。你只需要像列表和字符串一样使用该对象就可以了。

    还记得前面字符串里面的repr函数吗?它会创建一个字符串,以合法的Python表达式的形式来表示值。它也是多态特性的代表之一,我们可以对任何东西使用。

    很多函数饿运算符都是多态的,在多态的领域里面真正重要的是如何让对象按照你希望的方式工作,不管它是否是正确的类型。但是,有些函数或者方法能够毁掉多态--使用函数显示地检查类型,如type、isintance、issubclass等,我们要尽可能的避免这些方式。

    3.封装

   封装是指向程序中的其他部分隐藏对对象具体实现细节的原则。和多态很像,它们都是抽象的原则,它们都会帮助处理程序而不用过多的关注多余细节,就像函数做的一样。

    但是不能就说封装和多态是一样的,这是不对的。多态可以让用户对于不知道是什么类的对象进行方法调用,而封装是可以不关心对象是如何构建的而直接进行使用。在这里涉及这样一个问题,基本上,我们都要对对象进行抽象,调用方法时候我们稍不注意就会改变前面讲过的全局变量,这样就会失去原本程序设计的意义。但是,我们也可以不用关心其他的东西,可以将其作为特性存储。正如方法一样,特性是作为变量构成对象的一部分,事实上方法更像是绑定到函数上的属性。

    4.类和类型

    4.1类的概念

    前面的内容中,类的概念已经出现了好多次,类到底是什么?我们可以运用它来做什么?一起来看看。

    想象这样一个例子,人类是一个非常通用的抽象类,具有很多的子类。可以将人类想象为一个大的集合,而我们所谓的黄种人、白种人、黑种人就是它的子集。当一个对象所属的类是另外一个对象所属类的子集的时候,前者就被称为后者的子类,所以黄种人就是人类的子类。相反,人类就是黄种人的超类。

扫描二维码关注公众号,回复: 1618922 查看本文章

    在面向对象的设计中,子类的关系四隐式的,因为一个类的定义取决于它所支持的方法。类的所有实例都会包含这些方法,所以子类的所有实例都有这些方法。定义子类只是定义更多的方法的过程。

    4.2创建和使用自己的类

    这里我们来看一看一个具体的实现程序,理解在这的好多概念。

class Province(object):
    memo='中国的23个省之一'#静态字段
    #实例
    def __init__(self,name,captial,leader,flag):#方法
        self.Name=name
        self.Captial=captial
        self.Leader=leader#动态字段
        self.__Thailand=flag#私有字段
#动态方法
    def sports_meet(self):
        return self.Name + '正在开运动会'
#将动态方法改为静态方法
    @staticmethod
    def Foo():
        print('省要带头反腐')
#将动态方法改为特性
    @property
    def Bar(self):
        print(self.Name)
    #利用动态方法去调用私有字段只读
    @property
    def Thailand(self):
        return self.__Thailand
    @Thailand.setter
    def Thailand(self,value):
        self.__Thailand=value



hb=Province('河北','石家庄','李阳','True')
sd=Province('山东','济南','王胜辉','False')
japan=Province('日本','冲绳','苍井空','True')


print(hb.Captial)#对象可以访问动态字段
print(Province.memo)#类可以访问静态字段
print(sd.memo)#对象也可以访问静态字段
print(sd.Name)#对象可以访问动态字段
print(hb.sports_meet())#对象可以访问动态方法
print(sd.sports_meet())#对象可以访问动态方法
Province.Foo()#类可以访问静态方法
hb.Bar#特性访问
print(japan.Thailand)#只读特性访问
japan.Thailand=False
print(japan.Thailand)

    在上述的程序里面,我们要关注下面这几点:

  • 类的创建:类的创建以关键字class开始,格式为class 类名。类我们有旧式类和新式类之分,新式类的语法中需要在整个程序开始之前这样写:__metaclass__=type(注意这里是双下划线)或者在你的类命名处这样写:class Province(object),这表示你自己创建的类继承于object,也就是说它是一个新式类。
  • 在类里面你可以定义很多函数来满足自己的需求,函数定义的方法和以前得一样,这里我们称这样的函数为动态方法。
  • 几乎在每个类的开始部分都会有一个初始化的函数:__init__(双下划线),在这个函数里面,我们可以定义一些我们即将用到的参数或者变量,在这个函数里面定义的参数我们称为动态字段,并通过self.变量名的方法去将其初始化,我们也称是实例化这个类。
  • 在创建好了类和需要的函数以及方法后,我们需要创建一个对象去调用或者引用它,hb=Province('河北','石家庄','李阳','True'),我们创建一个名为hb的对象,然后根据类中初始化函数的定义填入我们需要传入的数据,这样hb对象就创建了。类似的,我们也可以创建sd和japan对象。
  • self到底是什么?当我们运行print(hb.sports_meet())这个代码的时候,你会得到'河北正在开运动会',我们在调用hb对象的sports_meet函数的饿时候,hb自动将自己作为第一个参数传入函数,因此我们称它为self,显然这是self的用处和存在的必要性。如果没有它的话,成员就没办法访问它们要对其特性进行操作的对象本身了。self参数事实上是方法和函数的区别。绑定方法将它们的第一个参数绑定到所属的实例上面,因此无需显示提供该参数。当然也可以将特性绑定到一个普通函数上面,这样就没有特殊的self参数了。
  • 程序中我们注意到,如果在函数定义也就是一般的动态方法前面加上:@staticmethod关键字的话,这个就变成了静态方法。如果在前面加上@property关键字的话,这个就变成了特性。而且我们换注意到在初始化函数中我们有这样的字段:self.__Thailand=flag,称前面有双下划线的字段为私有字段。并且对于私有字段我们可以设置它的模式为只读:@Thailand.setter在类的语法中我们规定:1.对象能访问的有:动态字段、动态方法、静态字段。2:类可以访问静态字段、静态方法,不可访问动态字段。
  • 如果在初始化函数外部创建参数或者变量,memo='中国的23个省份之一',我们称其为静态字段。
  • Foo如果从动态方法改为静态方法以后,就不再需要self参数了,这是我们前面说过的区别。
  • 对于私有字段的只读特性访问时候要注意它的方法和参数的个数。

    4.3类的继承

    关于类的继承,我们还是以一个完整的程序来说明情况:

class Father(object):
    def __init__(self):
        self.Fname='ffff'
        print('father__init__')
    def Func(self):
        print('father.Func')
    def Bad(self):
        print('Father.抽烟喝酒烫头')

class son(Father):
    def __init__(self):
        self.Sname='ssss'
        print('son__init__')
        #第一种,不需要继承object,显式调用
        Father.__init__(self)
        #第二种,父系必须要继承object
        super(son,self).__init__()
    def Bar(self):
        print('Son.bar')

    def Bad(self):
        print('Son.抽烟喝酒')
s1=son()
s1.Bar()
s1.Func()
s1.Bad()

    我们可以很清楚的看到,这个程序里面有两个类的继承。首先我们的Father类继承与object,也就是我们的新类。然后是我们的son类继承于Father。关系就很明确了,son是Father的子类,Father是son的超类。接着我们创建一个关于一个son类的对象,跑一跑程序,看看你会发现什么?    

/usr/local/Cellar/python/3.6.4_3/Frameworks/Python.framework/Versions/3.6/bin/python3.6 "/Users/yangjiayuan/PycharmProjects/day/day06/Inherit of lei.py"
son__init__
father__init__
father__init__
Son.bar
father.Func
Son.抽烟喝酒

Process finished with exit code 0

    对象创建以后调用的过程中,很明显Bar方法是属于son类的,但是son类是继承与Father类,从而在对象调用son里面方法的时候不仅会调用到Father类的__init__函数,也会调用到Father类的__init__函数,从而你会看到一共有四个输出。理所当然的是,我们son类的对象也可以调用其父类里面方法,但是这样不会触发初始化函数。当父类和子类里面有名称相同的方法的时候,父类的方法会在继承与父类的子类中被重构,也就是被重写。运行程序后,我们可以明显地看到Bad方法被重写了,它的输出为子类里面定义的输出。

    还要注意的一点就是,在子类中初始化父类时候有两种方式:第一种父类不需要继承object,我们可以直接调用。第二种父类必须继承object。两种调用的方式和方法也是不一样的。在程序中已经标出来,很清楚。

    4.4新式类和经典类

    前面已经讲了新式类和旧式类的一些区别,我们再来看看:

class A(object):
    def __init__(self):
        print('This is A')
    def save(self):
        print('Save method form A')

class B(A):
    def __init__(self):
        print('This is B')

class C(A):
    def __init__(self):
        print('This is c')
    def save(self):
        print('Save method from --C--')

class D(B,C):
    def __init__(self):
        print('This is D')

W=D()
W.save()
/usr/local/bin/python2.7 /Users/yangjiayuan/PycharmProjects/day/day06/lei_of_new_old.py
This is D
Save method from --C--

Process finished with exit code 0

    对上面代码进行修改,将A类修改为经典类,去掉继承于object的关系,再来看看结果:

/usr/local/bin/python2.7 /Users/yangjiayuan/PycharmProjects/day/day06/lei_of_new_old.py
This is D
Save method form A

Process finished with exit code 0

    这里的关系是,B类和C类都是继承与A类,D类同时继承于B类和C类。新式类和经典类有个叫深度优先和广度优先的说法。经典类是深度优先,当创建D类的对象并调用A类里面的方法时候,查找save方法路径是这样的:D-B-A-C,当找到save方法时候打印结果。新式类是广度优先,搜索路径是:D-B-C-A,其中B类和C类的顺序是按照程序里面继承顺序来写的。当找到save方法的时候打印出结果。这里D类涉及到多重继承,注意前面强调到的一些关系和处理方法就好。

    5.结束语

    关于类的知识就先介绍到这里,自己还是要多动手实践。最近也要开始忙着写论文咯,可能更新的速度会慢一些,希望大家谅解。下一章节预告--异常处理。




猜你喜欢

转载自blog.csdn.net/qq_34454366/article/details/80293984
今日推荐