Python 基础 面向对象之二 三大特性

Python 基础 面向对象之二 三大特性

    上一篇主要介绍了Python中,面向对象的类和对象的定义及实例的简单应用,本篇继续接着上篇来谈,在这一篇中我们重点要谈及的内容有:Python 类的成员、成员修饰符 面向对象的三大特性:继承、多态和封装,貌似今天内容挺多的,没有关系,慢慢来!

一、类中的基本知识:

    一、类的成员、成员修饰符

       

       

        一、字段

                  字段包括:普通字段和静态字段,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同

  •           普通字段属于对象
  •           静态字段属于类   
 1 class Room:
 2     tag=1
 3     def __init__(self,name,owner,width,length,heigh):
 4         self.name=name
 5         self.owner=owner
 6         self.width=width
 7         self.length=length
 8         self.heigh=heigh
 9 
10 
11 
12 r1=Room('厕所','sb',100,100,100000)   
13 print(r1.name)  # 访问的是普通字段
14 print(Room.tag) # #直接访问静态字段

    so,通过上述例子可以看出:

  •     静态字段在内存中只保存一份
  •     普通字段在每个对象中都要保存一份(相当于绑定在每个实例化对象的身上,然后本身有一个类“指针”的东西指向类)

    应用场景: 通过类创建对象时,如果每个对象都具有相同的字段,那么就使用静态字段

    二、方法

        方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同

  •     普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self
  •     类方法:由调用; 至少一个cls参数;执行类方法时,自动将调用该方法的复制给cls
  •     静态方法:由调用;无默认参数;
class Foo:

    def __init__(self, name):
        self.name = name

    def ord_func(self):
        """ 定义普通方法,至少有一个self参数 """

        # print self.name
        print ('普通方法')

    @classmethod
    def class_func(cls):
        """ 定义类方法,至少有一个cls参数 """

        print ('类方法')

    @staticmethod
    def static_func():
        """ 定义静态方法 ,无默认参数"""

        print ('静态方法')


# 调用普通方法
f = Foo('sb')
f.ord_func()

# # 调用类方法
Foo.class_func()
f.class_func()
#
# # 调用静态方法
Foo.static_func()
f.static_func()

    类方法调用和静态方法调用的时候既可以使用使用类名调用,也可以使用对象来调用,区别在哪里呢?注意:最大的区别是用类名调用的时候是不需要实例化的,而使用对象来访问时是先要进行实例化,之后再进行调用。

    三、属性  

      如果你已经了解Python类中的方法,那么属性就非常简单了,因为Python中的属性其实是普通方法的变种。

      对于属性,有以下三个知识点:

  •     属性的基本使用
  •     属性的两种定义方式
 1 class Goods:
 2     @property
 3     def pricr(self):
 4         #print('nijiushidahbi')
 5         return "haishininiubi"
 6 
 7 obj = Goods()
 8 result = obj.pricr
 9 print(result)
10 
11 
12 显示结果:haishininiubi

    由属性的定义和调用要注意一下几点:

  •     定义时,在普通方法的基础上添加 @property 装饰器;
  •     定义时,属性仅有一个self参数
  •     调用时,无需括号
               方法:foo_obj.func()
               属性:foo_obj.prop

    注意:属性存在意义是:访问属性时可以制造出和访问字段完全相同的假象

             属性由方法变种而来,如果Python中没有属性,方法完全可以代替其功能。

            Python的属性的功能是:属性内部进行一系列的逻辑计算,最终将计算结果返回

二、面向对象编程的三大特性介绍

       1.继承与派生

             继承是一种创建新的类的方式,在python中,新建的类可以继承自一个或者多个父类,原始类称为基类或超类,新建的类称为派生类或子类。

             python中类的继承分为:单继承和多继承

1 class Grandfather:
2     pass
3 
4 class Father(Grandfather): #单继承 Father是派生类,grandfather 是基类
5     pass
6 
7 class son(Father,Grandfather): #多继承
8     pass

            继承使用产生的场景:如果我们定义了一个类A,然后又想新建立另外一个类B,但是类B的大部分内容与类A的相同时我们不可能从头开始写一个类B,这就用到了类的继承的概念。通过继承的方式新建类B,让B继承A,B会‘遗传’A的所有属性(数据属性和函数属性),实现代码重用。

            除了继承与派生的概念还有一个概念是需要提出的那就是:组合。组合就是指在一个类中以另外一个类的对象作为数据属性,成为类的组合。

            组合与继承两者到底什么的关系?1.继承的方式:通过继承建立了派生类与基类的关系,两者之间是“”的关系。当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好,比如教授是老师    2.组合的方式:用组合的方式建立了类与组合的类之间的关系,它是一种‘’的关系,比如教授有生日,教授教python课程(当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好

class School:
    def __init__(self,name,addr):
        self.name=name
        self.addr=addr

    def zhao_sheng(self):
        print('%s 正在招生' %self.name)

class Course:
    def __init__(self,name,price,period,school):
        self.name=name
        self.price=price
        self.period=period
        self.school=school

s1=School('北大','北京') 

c1=Course('linux',10,'1h',s1)
print(c1.name) #linux
print(c1.school) # <__main__.School object at 0x02182330>
print(c1.school.name) # 北大
print(c1.school.addr) # 北京
View Code

         在这个例子中呢,创建了两个类,一个是学校类一个是课程类。需要将课程类与学校类进行相关联的时候,继承在这里是不合适的,那我们采用组合的方式来对这两个类进行关联,这样就在实例化一门课程的时候把学校的信息进行关联。

          接口的概念:

                继承有两种用途:

                    一:继承基类的方法,并且做出自己的改变或者扩展(解决代码重用,但是子类与基类出现强耦合

                    二:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能。(推举使用)

       归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。归一化,让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。

       接口继承:就是定义一个父类,其主要目的就是所有的子类,必须去实现父类的方法(与是否省代码,么有半毛钱关系),父类可以不去具体实现,具体的实现由子类自己去实现。

      举个例子:我们有一个汽车接口,里面定义了汽车所有的功能,然后由本田汽车的类,奥迪汽车的类,大众汽车的类,他们都实现了汽车接口,这样就好办了,大家只需要学会了怎么开汽车,那么无论是本田,还是奥迪,还是大众我们都会开了,开的时候根本无需关心我开的是哪一类车,操作手法(函数调用)都一样。

  抽象类: 从实现角度来看,抽象类与普通类的不同之处在于:抽象类中只能有抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法(这种解释有点像接口了)

#一切皆文件
import abc #利用abc模块实现抽象类

class All_file(metaclass=abc.ABCMeta):
    all_type='file'
    @abc.abstractmethod #定义抽象方法,无需实现功能
    def read(self):
        '子类必须定义读功能'
        pass

    @abc.abstractmethod #定义抽象方法,无需实现功能
    def write(self):
        '子类必须定义写功能'
        pass

# class Txt(All_file):
#     pass
#
# t1=Txt() #报错,子类没有定义抽象方法

class Txt(All_file): #子类继承抽象类,但是必须定义read和write方法
    def read(self):
        print('文本数据的读取方法')

    def write(self):
        print('文本数据的读取方法')

class Sata(All_file): #子类继承抽象类,但是必须定义read和write方法
    def read(self):
        print('硬盘数据的读取方法')

    def write(self):
        print('硬盘数据的读取方法')

class Process(All_file): #子类继承抽象类,但是必须定义read和write方法
    def read(self):
        print('进程数据的读取方法')

    def write(self):
        print('进程数据的读取方法')

wenbenwenjian=Txt()

yingpanwenjian=Sata()

jinchengwenjian=Process()

#这样大家都是被归一化了,也就是一切皆文件的思想
wenbenwenjian.read()
yingpanwenjian.write()
jinchengwenjian.read()

print(wenbenwenjian.all_type)
print(yingpanwenjian.all_type)
print(jinchengwenjian.all_type)
View Code

    继承实现的原理:(多继承,很多语言只支持单继承)

        Python继承中有两种方式:一种是深度优先;另一种是广度优先;

        当类是经典类时,多继承会按照深度优先方式查找;当类是新式类时,多继承会按照广度优先方式查找。注:当前类或者父类继承了object类,那么该类便是新式类,否则便是经典类。

      现在举个例子进行说明:

class A(object):
    def test(self):
        print('from A')

class B(A):
    def test(self):
        print('from B')

class C(A):
    def test(self):
        print('from C')

class D(B):
    def test(self):
        print('from D')

class E(C):
    def test(self):
        print('from E')

class F(D,E):
    # def test(self):
    #     print('from F')
    pass
f1=F()
f1.test()
print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性

#新式类继承顺序:F->D->B->E->C->A
#经典类继承顺序:F->D->B->A->E->C
#python3中统一都是新式类
#pyhon2中才分新式类与经典类
View Code

        在查找的过长中一旦找到查找过程就会立即中断,不会继续查找。

    多继承的原理:(Python实现多继承的原理)

        python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,例如:

F.mro() #等同于F.__mro__
[<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'objec
t
'>]

    子类调用父类方法的实现过程:子类继承了父类的方法,然后想进行修改注意了是基于原有的基础上修改,那么就需要在子类中调用父类的方法。

    方法一:父类名.父类方法()

class Vehicle: #定义交通工具类
     Country='China'
     def __init__(self,name,speed,load,power):
         self.name=name
         self.speed=speed
         self.load=load
         self.power=power

     def run(self):
         print('开动啦...')

class Subway(Vehicle): #地铁
    def __init__(self,name,speed,load,power,line):
        Vehicle.__init__(self,name,speed,load,power) #子类继承父类
        self.line=line

    def run(self):
        print('地铁%s号线欢迎您' %self.line)
        Vehicle.run(self)

line13=Subway('中国地铁','180m/s','1000人/箱','',13)
line13.run()


# 地铁13号线欢迎您
开动啦...
View Code

    方法二:super()(推举使用此种方式)

class Vehicle: #定义交通工具类
     Country='China'
     def __init__(self,name,speed,load,power):
         self.name=name
         self.speed=speed
         self.load=load
         self.power=power

     def run(self):
         print('开动啦...')

class Subway(Vehicle): #地铁
    def __init__(self,name,speed,load,power,line):
        #super(Subway,self) 就相当于实例本身 在python3中super()等同于super(Subway,self)
        super().__init__(name,speed,load,power)
        self.line=line

    def run(self):
        print('地铁%s号线欢迎您' %self.line)
        super(Subway,self).run()

class Mobike(Vehicle):#摩拜单车
    pass

line13=Subway('中国地铁','180m/s','1000人/箱','',13)
line13.run()
View Code

    注意:使用super调用的所有属性,都是从MRO列表当前的位置往后找,千万不要通过看代码去找继承关系,一定要看MRO列表。

    2.多态(性)

        多态是指一个事物有多中形态(一个抽象类有多个子类,so 多态的概念依赖于继承),而多态性是指:指具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容的函数。

        在面向对象方法中一般是这样表述多态性:向不同的对象发送同一条消息,不同的对象在接收时会产生不同的行为(即方法)。也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。

        多态是反映在执行时的状态,在面向对象中,多态是怎么来得呢?首先,先定义一个基类,再定义一堆子类。其次,才能展现不同的类实例化出的实例去调用同一个方法。

 1 class H2O:
 2     def __init__(self,name,temperature):
 3         self.name=name
 4         self.temperature=temperature
 5     def turn_ice(self):
 6         if self.temperature < 0:
 7             print('[%s]温度太低结冰了' %self.name)
 8         elif self.temperature > 0 and self.temperature < 100:
 9             print('[%s]液化成水' %self.name)
10         elif self.temperature > 100:
11             print('[%s]温度太高变成了水蒸气' %self.name)
12     def aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(self):
13         pass
14 
15 class Water(H2O):
16     pass
17 class Ice(H2O):
18     pass
19 class Steam(H2O):
20     pass
21 
22 w1=Water('',25)
23 i1=Ice('',-20)
24 s1=Steam('蒸汽',3000)
25 
26 w1.turn_ice()
27 i1.turn_ice()
28 s1.turn_ice()

   3.封装

    装就是把东西包起来,而封的意思就是把口子封起来。为何要在编程的过程中使用这一特性呢?封装数据的作用就是为了保护隐私,而封装方法的作用就是为了隔离复杂程度。
    封装其实是有两个层面的意思:

    其一:第一个层面的封装(什么都不用做):创建类和对象会分别创建二者的名称空间,我们只能用类名.或者obj.的方式去访问里面的名字,这本身就是一种封装。(注意:对于这一层面的封装(隐藏),类名.和实例名.就是访问隐藏属性的接口

    其二:第二个层面的封装:类中把某些属性和方法隐藏起来(或者说定义成私有的),只在类的内部使用、外部无法访问,或者留下少量接口(函数)供外部访问。

    在Python中,用双下划线来实现隐藏属性(设置成私有的),(类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式)

class A:
    __N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N
    def __init__(self):
        self.__X=10 #变形为self._A__X
    def __foo(self): #变形为_A__foo
        print('from A')
    def bar(self):
        self.__foo() #只有在类内部才可以通过__foo的形式访问到.

f1 = A()
# f1.__foo() # 'A' object has no attribute '__foo'
f1.bar() # 通过在对象A中创建一个接口,在内部调用私有方法

好了,今天就搞到这里,明天继续!!!

 

 

 

 

 

 

 

 

 

 

       

 

 

       

 

 

 

 

          

 

       

 

 

 

 

 

 

 

 

 

 

 

   

   

 

posted @ 2017-04-30 18:37  还是牛  阅读(327)  评论(0编辑  收藏  举报