Python学习笔记09_面向对象编程Object Oriented Programming

面向对象编程

  • 面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行。

  • 面向对象的程序设计把计算机程序视为一组对象的集合,每个对象可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。

    # 面向过程
    <<< std1 = {'name': 'Michael', 'score': 98}
    <<< std2 = {'name': 'Bob', 'score': 81}
    <<< def print_score(std):
            print('%s: %s' % (std['name'], std['score']))
    
    # 面向对象
    class Student(object):
    
        def __init__(self, name, score):
            self.name = name
            self.score = score
        
        def print_socre(self):
            print('%s %s' % (self.name, self.score))
        
    <<< bart = Student('Bart Simpson', 59)
    <<< lisa = Student('Lisa Simpson', 87)
    <<< bart.print_score()
    <<< bart.print_score()
    

类和实例

  • 和普通的函数相比,在类中定义的函数第一个参数永远是实例变量self,并且在调用时不用传递该参数。
  • 数据封装,方法是与实例绑定的函数,和普通函数不同,方法可以直接访问实例的数据,通过在实例上调用方法,我们就直接操作了对象内部的数据,但无需知道方法内部的实现细节。

访问限制

  • 如果要让类的内部属性不被外部访问,可以把属性的名称前加上两个下划线__。不能直接访问__name是因为Python解释器对外把__name变量改成了_Student__name,所以仍然可以通过_Student__name来访问__name变量。
    class Student(object):
    
        def __init__(self, name, score):
            self.__name = name
            self.__score = score
        
        def print_socre(self):
            print('%s %s' % (self.__name, self.__score))
        
        def get_name(self):
            return self.__name
        
        def get_score(self):
            return self.__score
        
        def set_score(self, score):
            if 0 <= score <= 100:
                self.__score = score
            else:
                raise ValueError('bad score')
    
  • 类似__xxx__的,是特殊变量,是可以直接访问的。
  • 类似_xxx的,单下划线开头的实例变量名,外部是可以直接访问的,约定俗成’虽然我可以被访问,但是,请把我视为私有变量,不要随意访问’。

继承和多态

  • 开闭原则,对扩展开放,允许新增Animal子类;对修改封闭,不需要修改依赖Animal类型的run_twice()函数。
  • 动态语言的"鸭子类型",它并不要求严格的继承体系,一个对象只要"看起来像鸭子,走起路来像鸭子",那么它就可以被看做是鸭子。Python的"file-like object"就是一种鸭子类型,对真正的文件对象,它有一个read()方法,返回其内容,但是,许多对象,只要有read()方法,都被视为"file-like object"。许多函数接收的参数就是"file-like object",你不一定要传入真正的文件对象,完全可以传入任何实现了read()方法的对象。

获取对象信息

  • 判断对象类型,使用type()函数,可判断基本类型、函数、类,type()函数返回对应的Class类型。

  • 判断一个对象是否是函数。

    <<< import types
    <<< def fn():
            pass
            
    <<< type(fn) == types.FunctionType
    True
    <<< type(abs) == types.BuiltinFunctionType
    True
    <<< type(lambda x: x) == types.LambdaType
    True
    <<< type((x fir x in range(10))) == types.GeneratorType
    
  • 判断class的类型,可以使用isinstance()函数。

    # 继承关系是 object -> Animal -> Dog -> Husky
    <<< a = Animal()
    <<< d = Dog()
    <<< h = Husky()
    <<< isinstance(h, Husky)
    <<< isinstance(h, Dog)
    <<< isinstance(h, Animal)
    True
    <<< isinstance(d, Husky)
    False
    
    <<< isinstance([1, 2, 3], (list, tuple))
    True
    
  • dir()函数,获得一个对象的所有属性和方法,它返回一个包含字符串的list。

  • getattr()setattr()hasattr(),直接操作一个对象的状态。

实例属性和类属性

  • 在编写程序的时候,不要把实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性。
    del s.name # 删除实例的name属性
    

使用__slots__

  • 给实例绑定属性和方法,给类绑定实例和方法。

    class Student(object):
        pass
    
    <<< 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)
    25
    
    <<< s2 = Student() # 创建新的实例
    <<< s2.age(25) # 尝试调用方法
    AttributeError:……
    
    # 给class绑定方法
    <<< def set_score(self, score)
            self.score = score
            
    <<< Student.set_score = set_score
    
    <<< s.set_score(100)
    <<< s.score
    100
    <<< s2.set_score(99)
    <<< s2.score
    99
    
  • 使用__slots__限制实例的属性,__slots__定义的属性仅对当前类起作用,对继承的子类不起作用。除非在子类中也定义__slots__,这样,子类允许定义的属性就是自身的__slots__加上父类的__slots__

    class Student(object):
        __slots__ = ('name', 'age')
    
    <<< s = Student() # 创建新的实例
    <<< s.name = 'Michael' # 绑定属性'name'
    <<< s.age = 25 # 绑定属性'age'
    <<< s.score = 99 # 绑定属性'score'
    AttributeError:……
    

使用@property

  • @property,只定义getter方法,不定义setter方法就是一个只读属性。

    @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
    
    @property
    def birth(self):
        return self._birth
    
    @birth.setter
    def birth(self, value):
        self._birth = value
    
    @property
    def age(self):
        return 2015 - self._birth
    
    <<< s = Student()
    <<< s.score = 60 # OK, 实际转化为s.set_score(60)
    <<< s.score # OK, 实际转化为s.get_score
    

多重继承

  • MinIn
    class Dog(Mammal, RunnableMixIn, CarnivorousMixIn)
    

定制类

  • __str____str__返回用户看到的字符串,__repr__返回程序开发者看到的字符串。

    class Student(object):
        def __init__(self, name):
            self.name = name
        def __str__(self):
            return 'Student object(name = %s)' % self.name
        __repr__ = __srt__
    
  • __iter__,使一个类可以被作用于for ... in循环返回一个迭代对象,Python的for循环会不断调用该迭代对象的__next__方法拿到循环的下一个值。

    class Fib(object):
        def __init__(self):
            self.a, self.b = 0, 1 # 初始化两个计数器
        
        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)
    
  • __getitem,使一个类可以像list那样按照下标取出元素。__setitem__delitem__

    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
    
    # 切片
    class Fib(object):
        def __getitem__(self, n):
            if isinstance(n, int): # n是索引
                a, b =1, 1
                for x in range(n):
                    a, b = b, a+b
                return a
            if isinstance(n, slice): # n是切片
                start = n.start
                stop = n.stop
                if start is None:
                    start = 0
                a, b = 1, 1
                L = []
                for x in range(stop):
                    if x >= start:
                        L.append(a)
                    a, b = b, a+b
                return L
    
    <<< f = Fib()
    <<< f[0:5]
    [1, 1, 2, 3, 5]
    
  • __getattr__,动态获取属性和方法。在没有找到属性的情况下,才调用__getattr__。任意调用s.abc都会返回None,因为我们定义的__getattr__默认返回就是None。要让class只响应特定的几个属性,我们要按照约定,抛出AttributeError的错误。

    class Student(object):
        def __init__(self):
            self.name = 'Michael'
            
        def __getattr(self, attr):
            if attr == 'score':
                return 99
            if attr == 'age':
                return lambda: 25
            raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr)
    
    <<< s = Student()
    <<< s.name
    'Michael'
    <<< s.score
    99
    <<< s.age()
    25
    
  • __call__,实现调用实例方法直接在实例本身上调用。_call_也可以定义参数,对实例调用好比对一个函数调用,可以把对象看成函数,把函数看成对象。因为类的实例是运行期创建出来的,如果你把对象看成函数,那么函数本身也可以在运行期动态创建出来。判断一个对象是否能被调用,能被调用的对象就是一个callable对象。

    class Student(object):
        def __init__(self, name):
            self.name = name
        
        def __call__(self):
            print('My name is %s.' % self.name)
    
    <<< s = Student('Michael')
    <<< s()
    My name is Michael.
    
    <<< callable(Student())
    True
    <<< callabke(max)
    True
    <<< callable([1, 2, 3])
    False
    <<< callable(None)
    False
    <<< callable('str')
    False
    

枚举类

  • Enum

    <<< from enum import Enum
    <<< Week = Enum('Week',('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'))
    <<< from name, member in Week.__members__.items():
            print(name, '=>', member, ',', member.value)
    
    from enum import Enum, unique
    
    @unique # @unique装饰器帮助检查没有重复值
    class Weekday(Enum):
        Sun = 0
        Mon = 1
        Tue = 2
        Wed = 3
        Thu = 4
        Fri = 5
        Sat = 6
    
    <<< day1 = Weekday
    <<< print(day1)
    Weekday.Monday
    <<< print(Weekday.Tue)
    Weekday.Tue
    <<< print(Weekday['Tue'])
    Weekday.Tue
    <<< print(Weekday.Tue.value)
    2
    <<< print(day1 == Weekday.Mon)
    True
    <<< print(day1 == Weekday.Tue)
    False
    <<< print(Weekday(1))
    Weekday.Mon
    <<< print(day1 == Weekday(1))
    True
    <<< Weekday(7)
    ValueError
    <<< for name, member inWeekday.__members__.items():
            print(name, '=>' member)
    Sun => Weekday.Sun
    Mon => Weekday.Mon
    Tue => Weekday.Tue
    Wed => Weekday.Wed
    Thu => Weekday.Thu
    Fri => Weekday.Fri
    Sat => Weekday.Sat
    

元类

  • type()

    # hello.py
    class Hello(object):
        def hello(self, name='world'):
            print('Hello, %s.' % name)
    
    <<< form helllo import Hello
    <<< h = Hello()
    <<< h.hello()
    Hello, world.
    <<< print(type(Hello))
    <class 'type'>
    <<< print(type(h))
    <class 'hello.Hello'>
    
    # type()函数创建新的类型
    <<< def fn(self, name='world'):
            print('Hello, %s.' % name)
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值