• Python面向对象编程


    一、类和实例

    类(Class)是抽象的模板
    实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同。

    1. 创建实例

    由于类可以起到模板的作用,因此,可以在创建实例的时候,把一些我们认为必须绑定的属性强制填写进去。通过定义一个特殊的__init__方法,在创建实例的时候,就把name,score等属性绑上去:

    class Student(object):
    
        def __init__(self, name, score):
            self.name = name
            self.score = score
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2. 数据封装

    直接在Student类的内部定义访问数据的函数,这样,就把“数据”给封装起来了。

    class Student(object):
    
        def __init__(self, name, score):
            self.name = name
            self.score = score
    
        def print_score(self):
            print('%s: %s' % (self.name, self.score))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    枚举类

    from enum import Enum
    
    Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
    
    • 1
    • 2
    • 3

    @unique装饰器可以帮助我们检查保证没有重复值。

    元类metaclass

    当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例。
    如果我们想创建出类呢?那就必须根据metaclass创建出类,所以:先定义metaclass,然后创建类。

    二、访问限制

    如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问

    class Student(object):
    
        def __init__(self, name, score):
            self.__name = name
            self.__score = score
    
        def print_score(self):
            print('%s: %s' % (self.__name, self.__score))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    注意:在Python中,变量名类似__xxx__的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量

    三、继承和多态

    1. 定义

    当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。
    当子类和父类都存在相同的run()方法时,我们说,子类的run()覆盖了父类的run(),在代码运行的时候,总是会调用子类的run()。这样,就是:多态。

    2. “开闭”原则:

    • 对扩展开放:允许新增Animal子类;
    • 对修改封闭:不需要修改依赖Animal类型的run_twice()等函数。

    3. 静态语言 vs 动态语言

    对于静态语言(例如Java)来说,如果需要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,否则,将无法调用run()方法。

    对于Python这样的动态语言来说,则不一定需要传入Animal类型。我们只需要保证传入的对象有一个run()方法就可以了

    4. 多重继承

    通过多重继承,一个子类就可以同时获得多个父类的所有功能。

    class Bat(Mammal, Flyable):
        pass
    class MyTCPServer(TCPServer, ForkingMixIn):
        pass
    
    • 1
    • 2
    • 3
    • 4

    四、获取对象信息

    1. 使用type()

    基本类型都可以用type()判断:

    >>> type(123)
    
    >>> type('str')
    
    >>> type(None)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    如果一个变量指向函数或者类,也可以用type()判断

    >>> type(abs)
    
    >>> type(a)
    
    
    • 1
    • 2
    • 3
    • 4

    2. 使用isinstance()

    >>> a = Animal()
    >>> d = Dog()
    >>> h = Husky()
    >>>> isinstance(h, Husky)
    True
    >>> isinstance(h, Dog)
    True
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    并且还可以判断一个变量是否是某些类型中的一种,比如下面的代码就可以判断是否是list或者tuple:

    >>> isinstance([1, 2, 3], (list, tuple))
    True
    >>> isinstance((1, 2, 3), (list, tuple))
    True
    
    • 1
    • 2
    • 3
    • 4

    3. 使用dir()

    如果要获得一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list

    >>> dir('ABC')
    ['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']
    
    • 1
    • 2

    五、实例属性和类属性

    给实例绑定属性的方法是通过实例变量,或者通过self变量

    class Student(object):
        def __init__(self, name):
            self.name = name
    
    s = Student('Bob')
    s.score = 90
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    直接在class中定义属性,这种属性是类属性,归Student类所有

    class Student(object):
        name = 'Student'
    
    • 1
    • 2

    当我们定义了一个类属性后,这个属性虽然归类所有,但类的所有实例都可以访问到。

    六、使用__slots__

    当我们定义了一个class,创建了一个class的实例后,我们可以给该实例绑定任何属性和方法,这就是动态语言的灵活性。
    但是,给一个实例绑定的方法,对另一个实例是不起作用的。

    >>> s = Student()
    >>> s.name = 'Michael' # 动态给实例绑定一个属性
    >>> print(s.name)
    Michael
    >>> def set_age(self, age): # 定义一个函数作为实例方法
    ...     self.age = age
    ...
    >>> from types import MethodType
    >>> s.set_age = MethodType(set_age, s) # 给实例绑定一个方法
    >>> s.set_age(25) # 调用实例方法
    >>> s.age # 测试结果
    25
    
    >>> s2 = Student() # 创建新的实例
    >>> s2.set_age(25) # 尝试调用方法
    Traceback (most recent call last):
      File "", line 1, in 
    AttributeError: 'Student' object has no attribute 'set_age'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    为了给所有实例都绑定方法,可以给class绑定方法:

    >>> def set_score(self, score):
    ...     self.score = score
    ...
    >>> Student.set_score = set_score
    
    • 1
    • 2
    • 3
    • 4

    给class绑定方法后,所有实例均可调用:

    >>> s.set_score(100)
    >>> s.score
    100
    >>> s2.set_score(99)
    >>> s2.score
    99
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性:

    class Student(object):
        __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
        
    >>> s = Student() # 创建新的实例
    >>> s.name = 'Michael' # 绑定属性'name'
    >>> s.age = 25 # 绑定属性'age'
    >>> s.score = 99 # 绑定属性'score'
    Traceback (most recent call last):
      File "", line 1, in 
    AttributeError: 'Student' object has no attribute 'score'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    七、@property

    @property装饰器就是负责把一个方法变成属性调用的:

    class Student(object):
    
        @property
        def score(self):
            return self._score
    
        @score.setter
        def score(self, value):
            if not isinstance(value, int):
                raise ValueError('score must be an integer!')
            if value < 0 or value > 100:
                raise ValueError('score must between 0 ~ 100!')
            self._score = value
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    八、定制类

    __len__()方法:是为了能让class作用于len()函数。
    __str__:返回一个描述的字符串

    >>> class Student(object):
    ...     def __init__(self, name):
    ...         self.name = name
    ...     def __str__(self):
    ...         return 'Student object (name: %s)' % self.name
    ...
    >>> print(Student('Michael'))
    Student object (name: Michael)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    __iter__:返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。

    class Fib(object):
        def __init__(self):
            self.a, self.b = 0, 1 # 初始化两个计数器a,b
    
        def __iter__(self):
            return self # 实例本身就是迭代对象,故返回自己
    
        def __next__(self):
            self.a, self.b = self.b, self.a + self.b # 计算下一个值
            if self.a > 100000: # 退出循环的条件
                raise StopIteration()
            return self.a # 返回下一个值
    >>> for n in Fib():
    ...     print(n)
    ...
    1
    1
    2
    3
    5
    ...
    46368
    75025
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    __getitem__:要表现得像list那样按照下标取出元素

    class Fib(object):
        def __getitem__(self, n):
            a, b = 1, 1
            for x in range(n):
                a, b = b, a + b
            return a
            
    >>> f = Fib()
    >>> f[0]
    1
    >>> f[1]
    1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    __getattr__:动态返回一个属性

    class Student(object):
    
        def __init__(self):
            self.name = 'Michael'
    
        def __getattr__(self, attr):
            if attr=='score':
                return 99
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    __call__:任何类,只需要定义一个__call__()方法,就可以直接对实例进行调用。

    class Student(object):
        def __init__(self, name):
            self.name = name
    
        def __call__(self):
            print('My name is %s.' % self.name)
    >>> s = Student('Michael')
    >>> s() # self参数不要传入
    My name is Michael.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  • 相关阅读:
    vue中预览epub文件
    掌动智能:云可观测性的主要特点及应用场景
    Vue.use的实现原理
    广汽埃安完成183亿融资:估值千亿 人保资本与国调基金是股东
    使用Windows数据收集器收集网站运行指标
    关于JAVA进阶你需要学习什么?
    13.1 使用DirectX9绘图引擎
    【踩坑】.NET异步方法不标记async,Task<int> 返回值 return default问题
    python案例:百钱买鸡
    pytest测试框架使用基础07 fixture—parametrize获取参数的几种常用形式
  • 原文地址:https://blog.csdn.net/guoxulieying/article/details/133907189