Python面向对象编程笔记

上篇

class Animal():
   def __init__(self,name,speed):
       self.name = name # 动物名字
       self.speed = speed # 动物行走或飞行速度
  1. self的作用是指明这两个数据是实例上的,而非类上的。
  2. 注意到__init__也有参数self,说明这个也是实例上的方法(而非类的)
class Animal():
   def __init__(self,name,speed):
       self.name = name # 动物名字
       self.speed = speed # 动物行走或飞行速度
  
   def __str__(self):
        return '''Animal({0.name},{0.speed}) is printed
                name={0.name}
                speed={0.speed}'''.format(self)
  1. 其中0.name等,是类专有的打印格式
  2. 返回
Animal(加菲猫,8) is printed
                name=加菲猫
                speed=8

类可以直接拥有属性,例如:

class Animal():
   cprop = "我是类上的属性cprop"

该属性可以被直接引用且继承,比如

In [1]: Animal.cprop                                                           
Out[1]: '我是类上的属性cprop'

In [1]: cat = Animal('加菲猫',8)
In [2]: cat.cprop                                                              
Out[2]: '我是类上的属性cprop'

也可以直接给类或者实例添加一个之前不存在的属性(赋值会自动创建)

cat.color = 'grap'#定义的Animal类只有name和speed两个属性

In [24]: hasattr(cat,'color') # cat 已经有`color`属性                          
Out[24]: True

可以在类内设置私有属性,仅类内方法可以使用该属性。

class Manager():
    def __init__(self,animal):
        self.animal = animal
        
    def recordTime(self):
        self.__t = time.time()
        print('feeding time for %s(行走速度为:%s) is %.0f'%(self.animal.name,self.animal.speed,self.__t))
    
    def getFeedingTime(self):
        return '%0.f'%(self.__t,) 

此处self__t表示类的私有属性,只能被该类之内的方法引用(产生继承也无法引用),t前两个下横杠

多态

import time
from animal import (Animal,Cat,Bird)

class Manager():
    def __init__(self,animal):
        self.animal = animal
        
    def recordTime(self):
        self.__t = time.time()
        if isinstance(self.animal, Cat):
            print('feeding time for %s is %.0f'%(self.animal.name,self.__t))
            self.animal.getRunningSpeed()
        if isinstance(self.animal,Bird):
            print('feeding time for %s is %.0f'%(self.animal.name,self.__t))
            self.animal.getFlyingSpeed()

    def getFeedingTime(self):
        return '%0.f'%(self.__t,)

当Manager分别继承Cat和Bird时,若继承的对象不同,则其中继承的某些函数方法也会不同,比如recordTime的功能是记录时间。对于Cat而言,方法是统计running的时间,而对于Bird而言,是统计Flying的时间,所以分别继承Cat和Bird时,recordTime时,对应不同对象,执行的小函数是不同的,如上代码所示,则需要先对继承的实例进行判断。

当使用多态时,则省去了写许多if的麻烦:首先在基类Animal中创建一个基方法(只执行pass),然后由Cat和Bird继承后重写此方法。

# animal2.py 模块
class Animal():
   cprop = "我是类上的属性cprop"
   
   def __init__(self,name,speed):
       self.name = name # 动物名字
       self._speed = speed # 动物行走或飞行速度
  
   def __str__(self):
        return '''Animal({0.name},{0._speed}) is printed
                name={0.name}
                speed={0._speed}'''.format(self)

   def getSpeedBehavior(self):
       pass 

class Cat(Animal):
    def __init__(self,name,speed,color,genre):
        super().__init__(name,speed)
        self.color = color 
        self.genre = genre
        
    # 重写方法
    def getSpeedBehavior(self):
        print('running speed of %s is %s' %(self.name, self._speed))
        return self._speed
        

class Bird(Animal):
    def __init__(self,name,speed,color,genre):
        super().__init__(name,speed)
        self.color = color 
        self.genre = genre

    # 重写方法
    def getSpeedBehavior(self):
        print('flying speed of %s is %s' %(self.name, self._speed))
        return self._speed

重写完后则Manager导入参数Animal

# manager2.py 模块
import time
from animal2 import (Animal,Cat,Bird)

class Manager():
    def __init__(self,animal):
        self.animal = animal
        
    def recordTime(self):
        self.__t = time.time()
        print('feeding time for %s is %.0f'%(self.animal.name,self.__t))
        self.animal.getSpeedBehavior()

    def getFeedingTime(self):
        return '%0.f'%(self.__t,)  

在实际使用时,将信息传入Cat类,再让Manager去继承,当执行Manager类recordTime方法时,就会指向Cat里的getSpeedBehavior

if __name__ == "__main__":
    jiafeimao = Cat('jiafeimao',2,'gray','CatGenre')
    haiying = Bird('haiying',40,'blue','BirdGenre')#创建了由Animal类继承且改写的实例

    Manager(jiafeimao).recordTime()
    print('#'*30)
    Manager(haiying).recordTime()  

下篇(进阶)

上篇中的多态重写方法并不是最好的,更优的方法是使用python内置的abc模块中的abstractmethod装饰器

import abc

class Animal():
   cprop = "我是类上的属性cprop"
   
   def __init__(self,name,speed):
       self.name = name # 动物名字
       self._speed = speed # 动物行走或飞行速度
  
   def __str__(self):
        return '''Animal({0.name},{0._speed}) is printed
                name={0.name}
                speed={0._speed}'''.format(self)
   
   # 使用abstractmethod装饰器后,变为抽象方法,指明需要重写此方法
   @abc.abstractmethod
   def getSpeedBehavior(self):
       pass

对类的属性赋值时,如果想要对取值范围加以限制,则可以使用property,先用property声明要限制的属性,再对声明的属性_speed使用setter函数,进行赋值检查。

  # 读
   @property 
   def _speed(self):#前面单下划线表示伪私有,外部可以访问,但声明了最好不要访问。
       return self.__speed
  # 写
   @_speed.setter
   def _speed(self,val):#对输入值进行检查
       if val < 0:
           raise ValueError('speed value is negative')
       self.__speed = val

我们可以对实例动态地添加属性,也可以给类添加属性,但添加类属性过多则会内存占用大,影响程序性能,因此可以使用系统魔法函数slots

class Student(object):
    __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称

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


s = Student('xiaoming',100) # 创建新的实例
s.score=10

链式调用

每一个对外公开的方法都返回self

当执行完s.set_name('xiaoming1')时,返回的self和 .set_age(25)又组成了新的语句self.set_age(25),形成了一个链式调用。

class Student(object):
    __slots__ = ('name', 'age')  # 用tuple定义允许绑定的属性名称

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

    def set_name(self, val):
        self.name = val
        return self

    def set_age(self, age):
        self.age = age
        return self

    def print_info(self):
        print("name: " + self.name)
        print("age: " + str(self.age))
        return self


s = Student('xiaoming', 100)  # 创建新的实例

(
    s
    .set_name('xiaoming1')
    .set_age(25)
    .print_info()
)

参考资料:Python与算法社区 ,作者zhenguo

posted @ 2020-08-09 09:52  Peskin  阅读(66)  评论(0)    收藏  举报