美文网首页Python
Python面向对象编程

Python面向对象编程

作者: Techml | 来源:发表于2017-04-20 17:13 被阅读0次

    1. 类相关知识
    2. 对象相关知识
    3. 类属性增删改查
    4. 实例属性增删改查
    5. 对象与实例属性
    6. 静态属性
    7. 类方法
    8. 静态方法
    9. 组合
    10. 继承
    11. 接口设计与归一化设计
    12. 继承顺序之mro线性顺序列表
    13. Python2中的继承顺序
    14. 子类调用父类的方法
    15. super调用父类的方法
    16. 多态
    17. 封装
    18. 反射
    19. 动态导入模块
    20. 类的内置属性attr
    21. 继承方式完成包装
    22. 组合方式完成授权
    23. getattribute
    24. item
    25. str与repr
    26. 自定制format
    27. slots属性
    28. doc属性
    29. module和class
    30. 析构方法
    31. call方法
    32. 迭代器协议
    33. 描述符理论
    34. 描述符优先级
    35. 类的装饰器
    36. 自定制property
    37. 元类
    38. 自定义元类

    类相关知识

    对象相关知识

    类属性增删改查

    实例属性增删改查

    对象与实例属性

    静态属性

    类方法

    静态方法

    组合

    继承

    接口设计与归一化设计

    聚成顺序之mro线性顺序列表

    Python2中的继承顺序

    子类调用父类的方法

    super调用父类的方法

    多态

    封装

    反射

    动态导入模块

    类的内置属性attr

    继承方式完成包装

    组合方式完成授权

    getattribute

    item

    str与repr

    自定制format

    slots属性

    doc属性

    module和class

    析构方法

    call方法

    迭代器协议

    描述符理论

    描述符优先级

    类的装饰器

    自定制property

    元类

    自定义元类

    一. 面向对象编程的一些特性

    相比于面向过程的编程方式,采用面向对象的编程方式可以提高代码的复用性和灵活性

    1. 复用性

    设计一个类,就是为了将这个类作为一个模块来使用,一个类多次被使用就提高了这个类的复用性,最终达到减少代码量以及修改一处处处更新的目的。提高代码复用性的方法有两个:组合派生

    组合

    组合就是将B类加入到A类中,两个类的关系就是A has B,通过这种方式来增强B类的功能和A类的代码重用性。

    class Course(object):
        def __init__(self, name, period):
            self.name = name
            self.period = period
    class Teacher:
        def __init__(self, name, gender, course):
            self.name = name
            self.gender = gender
            self.course = course
    
    c1 = Course('python', 7)
    t1 = Teacher('Hax', 'male', c1)
    print(t1.course.period)
    
    >>7
    

    Course类被组合进Teacher类,Teacher就拥有了Course类的所有属性和方法。

    派生

    子类继承了父类的属性,然后衍生出自己新的属性,如果子类衍生出的新的属性与父类的某个属性的名字相同,那么再调用子类的这个属性,就以子类的这个属性为准了。
    类特征可以在子孙类或子类中进行继承。这些子类从基类继承他们的核心属性,并且不影响父类的的特性。

    class Human(object):
        def __init__(self, name, gender, birthday):
            self.name = name
            self.gender = gender
            self.birthday = birthday
    
        def prt(self):
            print('from Human', self.name, self.gender, self.birthday)
    
        def flag(self):
            print('this is Human class')
    
    
    class Teacher(Human):
        def __init__(self, name, gender, birthday):
            Human.__init__(self, name, gender, birthday)
    
        def flag(self):
            print('this is Teacher class')
    
    h1 = Human('Ken', 'female', '2016/1/2')
    t1 = Teacher('Joe', 'male', '2017/1/1')
    
    h1.prt()
    t1.prt()
    h1.flag()
    t1.flag()
    
    >>
    from Human Ken female 2016/1/2
    from Human Joe male 2017/1/1
    this is Human class
    this is Teacher class
    

    flag方法在子类中实现了,调用时只调用子类的flag方法,而不影响父类的flag方法。prt方法在父类中定义,而在子类中没有定义,那么调用时父类子类都调用父类的prt方法。

    2.灵活性

    灵活性通过多态多态性实现
    多态是从类的定义的维度来定义的,以动物类为例,鸡、鸭、鱼、狗都是动物,但是是不同的动物,又都有动物的特性,即一类事物的不同形态。
    **多态性是从类方法的应用维度定义的。还是以动物类为例。所有的动物都有移动这个特性,移动这个特性包括:爬,游,走。为了方便应用可以定义一个函数接受不同的动物作为参数,执行相同的方法--移动,但执行的结果不同--爬,游,走,这就是多态性。

    class Animal(object):
        def __init__(self, sex, age):
            self.sex = sex
            self.age = age
    
        def move(self):
            pass
    
    class Fish(Animal):
        def __init__(self, sex, age):
            Animal.__init__(self, sex, age)
    
        def move(self):
            print('Fish is swimming')
    
    class Bird(Animal):
        def __init__(self, sex, age):
            Animal.__init__(self, sex, age)
    
        def move(self):
            print('Bird is flying')
    
    f = Fish('female', 2)
    b = Bird('male', 3)
    
    def func(obj):
        obj.move()
    
    func(f)
    func(b)
    

    代码中定义的鱼和鸟就是动物类的多态表现,通过func 方法传入不同的动物的实例执行实例对应的方法就是多态性的体现

    3. 封装

    封装数据:保护隐私
    封装方法:隔离复杂度
    封装需要提供访问被封装内容的接口,接口调用者只能通过接口访问内部数据,并且不能访问被隐藏内容的细节。此外,可以在接口中设计逻辑,限制调用者的访问方式。
    封装分为两个层次
    第一个层次就是建立类,当类建立之后,调用者只能通过点的方式访问类内的数据。第二层次是通过
    第二个层次是将类内的方法或属性定义为私有的,只能在类内部调用,类外部是调用不到的,或者在有需求时通过设计接口的方式供类外部使用。

    使用双下划线设置私有变量

    class Teacher(object):
        def __init__(self, name, salary):
            self.name = name
            self.__salary = salary # 变形为_Teacher__salary
    
        def __set_salary(self, s): # 变形为_Teacher__set_salary
            self.__salary = s
    
        def get_salary(self):
            return self.__salary
    

    类中所有__ x都会变为_类名 __ xself.__x引用的是变形后的属性;这种变形的目的是在类的外部是无法通过类. __x对象.__x来访问到双下滑先开头定义的属性或方法的。</br>
    子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。</br>
    这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了。</br>
    变形的过程只在类的定义是发生一次,在定义后的赋值操作,不会变形</br>
    在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的

    # 不使用双下滑线定义方法,子类方法覆盖了父类方法
    class Human(object):
        def language(self):
            print('speak sth')
        def speak(self):
            self.language()
    
    class China(Human):
        def language(self):
            print('speak chinese')
    
    p = China()
    p.speak()
    
    >>speak chinese
    
    # 使用双下滑线定义方法,子类方法没有覆盖父类方法
    class Human(object):
        def __language(self):
            print('speak sth')
    
        def speak(self):
            self.__language()
    
    class China(Human):
        def __language(self):
            print('speak chinese')
    
    p = China()
    p.speak()
    
    >>speak sth
    

    python并不会真的阻止你访问私有的属性,模块也遵循这种约定,如果模块名以单下划线开头,那么from module import *时不能被导入,但是通过from module import _private_module依然是可以导入的

    property

    property是一种特殊的特性,使用该特性后对象调用方法时会使用像对象访问属性一样的方式

    import math
    class Circle:
        def __init__(self,radius): 
            self.radius=radius
    
        @property
        def area(self):
            return math.pi * self.radius**2 
    
        @property
        def perimeter(self):
            return 2*math.pi*self.radius 
    
    c=Circle(10)
    print(c.radius)
    print(c.area)   # 像访问数据属性访问area,实际执行函数功能,计算面积
    print(c.perimeter)  # 像访问数据属性访问perimeter,实际执行函数功能,计算周长
    

    在C++里一般会将所有的所有的数据都设置为私有的,然后提供set和get方法(接口)去设置和获取,在python中通过property方法可以实现

    class Foo:
        def __init__(self, val):
            self.__NAME = val #将所有的数据属性都隐藏起来
    
        @property
        def name(self):
            pass
    
        @name.setter
        def name(self, value):
            if not isinstance(value, str):
                raise TypeError('%s must be str' % value)
            self.__NAME = value
    
        @name.getter
        def name(self):
            return self.__NAME
    
        @name.deleter
        def name(self):
            raise TypeError('Can not delete')
    
    f = Foo('Jack')
    print(f.name)
    
    >>Jack
    

    上述例子通过@name.setter装饰的方法将私有属性在检查过类型后赋值给了self.__NAME,然后通过@name.getter装饰的方法方法获取到了私有属性。

    二. 类相关的知识点

    1. 类属性及方法的增删改查

    2. super

    super用于继承中,用来替换父类,简化代码,下面举例说明
    python2
    在介绍super前先引入一个概念:新式类经典类
    Python2中类有新式类经典类两种类,新式类是有父类的类,如果没有实际的父类就使用object作为父类;经典类是没有父类的类。
    python2中super方法只对新式类有效
    下面的例子就是新式类中super方法的使用范例

     class People(object):
         def __init__(self, name, gender, age):
             self.name = name
             self.gender = gender
             self.age = age
         def walk(self):
             print '%s is walking' % self.name
    
     class China(People):
         country = 'china'
         def __init__(self, name, gender, age, language='chinese'):
             super(China, self).__init__(name, gender, age)
             self.language = language
    

    super代替People并在其后加上当前类的类名Chinaself作为参数
    python3
    在``python3`中对语法进行了进一步的精简

    class People(object):
        def __init__(self, name, gender, age):
            self.name = name
            self.gender = gender
            self.age = age
    
        def walk(self):
            print('%s is walking' % self.name)
    
    class China(People):
        country = 'china'
    
        def __init__(self, name, gender, age, language='chinese'):
            super().__init__(name, gender, age)
            self.language = language
    

    可以看出将super后面的参数省去了

    3. 接口与归一化设计

    Python中没有接口的概念,为了实现接口设计,可以使用继承实现;为了实现强制子类实现接口功能,可以使用abc模块

    import abc
    
    
    class File(metaclass=abc.ABCMeta):
        @abc.abstractmethod
        def file_write(self):
            pass
    
        @abc.abstractmethod
        def file_read(self):
            pass
    
    
    class Text(File):
        def file_write(self):
            print('text file execute write operation')
    
        def file_read(self):
            print('text file execute read operation')
    
    
    class Process(File):
        def file_write(self):
            print('process file execute write operation')
    
        def file_read(self):
            print('process file execute read operation')
    
    
    class Disk(File):
        def file_write(self):
            print('disk file execute write operation')
    
        def file_read(self):
            print('disk file execute read operation')
    

    4. 在类内部定义的函数无的三种用途

    绑定到对象的方法
    只要是在类内部定义的,并且没有被任何装饰器修饰过的方法,都是绑定到对象的

        class Foo:
            def test(self): #绑定到对象的方法
                pass
            def test1(): #也是绑定到对象的方法,只是对象.test1(),会把对象本身自动传给test1,因test1没有参数所以会抛出异常
                pass
    

    绑定到对象,指的是:由对象调用
    使用方式:对象.对象的绑定方法(),不用为self传值
    特性:调用时会把对象本身当做第一个参数传给对象的绑定方法

    绑定到类的方法:classmethod
    在类内部定义的,并且被装饰器@classmethod修饰过的方法,都是绑定到类的

        class Foo:
            def test(self): #绑定到对象的方法
                pass
            def test1(): #也是绑定到对象的方法,只是对象.test1(),会把对象本身自动传给test1,因test1没有参数所以会抛出异常
                pass
    f = Foo()
    f.test1()
    >>TypeError: test1() takes 0 positional arguments but 1 was given
    

    绑定到对象,指的是:就给对象去用,
    使用方式:对象.对象的绑定方法()
    特性:调用时会把对象本身当做第一个参数传给对象的绑定方法

    解除绑定的方法:staticmethod
    既不与类绑定,也不与对象绑定,不与任何事物绑定
    绑定的特性:自动传值(绑定到类的就是自动传类,绑定到对象的就自动传对象)
    解除绑定的特性:不管是类还是对象来调用,都没有自动传值这么一说了

    所以说staticmethod就是相当于一个普通的工具包

        
    class Foo:
        def test1(self):
            pass
        def test2():
            pass
        
        @classmethod
        def test3(cls):
            pass
        @classmethod
        def test4():
            pass
        
        @staticmethod
        def test5():
            pass
    

    test1与test2都是绑定到对象方法:调用时就是操作对象本身
    <function Foo.test1 at 0x0000000000D8E488>
    <function Foo.test2 at 0x0000000000D8E510>
    test3与test4都是绑定到类的方法:调用时就是操作类本身
    <bound method Foo.test3 of <class 'main.Foo'>>
    <bound method Foo.test4 of <class 'main.Foo'>>
    test5是不与任何事物绑定的:就是一个工具包,谁来都可以用,没说专门操作谁这么一说
    <function Foo.test5 at 0x0000000000D8E6A8>

    相关文章

      网友评论

        本文标题:Python面向对象编程

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