Day09 – 面向对象编程进阶

作者: paranoid 分类: Python 发布时间: 2021-01-26 23:33

日期:2021年1月26日

今天是学习Python的第九天,东西还是挺多了,开始使用小本本记一些代码和用法,方便记忆和查阅,今天没有练习,看几个案例。

学习用的教材是GitHub上骆昊编写的《Python - 100天从新手到大师》


一、@property装饰器

用一个下划线来提示自己类中的属性是受保护的,但是直接暴露给外界也是有问题的。可以使用@property包装器来包装getter(访问器)和setter(修改器)方法来对属性进行访问,安全且方便。

class Rect:
    def __init__(self,l,w):
        self._l=l
        self._w=w
    # 访问器 - getter方法
    @property
    def l(self):
        return self._l
    # 修改器 - setter方法
    @l.setter
    def l(self,l):
        self._l=l

    def distance(self):
        print('面积为:',self._l*self._w)

def main():
    s1=Rect(2,3)
    s1.distance()
    print(s1.l)
    s1.l=5
    print(s1.l)
    s1.distance()

if __name__ == "__main__":
    main()

💡 需要注意的就是,使用访问器用@property开头,然后return返回值;使用修改器使用@属性.setter开头,修改对应属性参数。主程序中需要调用的时候直接使用对象.属性即可

二、 slots 魔法

Python是一门动态语言,允许在程序运行中给对象绑定新的属性或方法,也可以对已绑定的部分进行解绑。如果需要限定自定义类型的对象只能绑定某些属性,可以通过定义__slots__来进行限定,且对子类没有作用。

以上面的例子为,在新建Rect类的时候添加一句__slots__==('_l','_w','_n')

类Rect的对象就限定只能绑定3个属性:_l_w_n

这时候主程序可以使用s1._n进行赋值,如果使用别的属性名称,如s1._t=1,就会报错AttributeError: 'Rect' object has no attribute '_t'

三、静态方法和类方法

静态方法:在调用时不需要类的实例(不需要self参数)

类方法:不需要对象实例,第一个参数约定为cls,代表当前类相关的信息的对象(类本身也是一个对象,有的地方称之为类的元数据对象),通过这个参数可以获取和类相关的信息并创建出类的对象。

静态方法(@staticmethod):

class 类名:
    # 创建静态方法
    @staticmethod
    def 方法名(参数):
            pass

def main():
    # 调用静态方法
    类名.方法名(参数)

if __name__ == "__main__":
    main()

类方法(@classmethod):

from time import localtime,sleep
from os import system

class Clock:
    def __init__(self,hour=0,minute=0,second=0):
        self._hour=hour
        self._minute=minute
        self._second=second
    # 创建类方法
    @classmethod
    def now(cls):
        ctime=localtime()
        return cls(ctime.tm_hour,ctime.tm_min,ctime.tm_sec)

    def run(self):
        self._second+=1
        if self._second==60:
            self._second=0
            self._minute+=1
            if self._minute==60:
                self._minute=0
                self._hour+=1
                if self._hour==24:
                    self._hour=0

    def output(self):
        while 1:
            self.run()
            system('cls')
            print('当前时间:%02d:%02d:%02d' % (self._hour,self._minute,self._second))
            sleep(1)

def main():
    # 调用类方法
    s=Clock.now()
    s.output()

if __name__=='__main__':
    main()

💡 普通方法、类方法、静态方法的区别:

method(普通方法):
通过实例调用时,可以引用类内部的任何属性和方法

classmethod(类方法):
无需实例化, 可以调用类属性和类方法,无法取到普通的成员属性和方法

staticmethod(静态方法):
无论用类调用还是用实例调用,都无法取到类内部的属性和方法, 完全独立的一个方法

四、类之间的关系

类之间有三种关系:is-a、has-a、use-a

  • is-a(继承),例如学生和人的关系

  • has-a(关联),例如学生和班级的关系;如果是整体和部分的关联,称之为聚合关系;如果整体进一步负责了部分生命的周期,称之为合成关系。

  • use-a(依赖),例如学生有一个看书的行为,其中使用到了教室,那么学生和教室就是依赖关系。

五、继承

子类可以继承父类提供的属性和方法,还可以定义自己特有的属性和方法。

  • 子类的定义:

    直接在括号内写上父类名class 子类(父类),多个父类使用逗号隔开。

  • 初始化:

    使用关键字super,在def __init__(self,父类参数,新参数):后加入super.__init__(父类参数)以调用父类的初始化函数,注意不需要写self,然后再下面初始化新参数。

写个学C++的例子:公司的工资管理

"""
请按照以下要求实现某个公司的工资管理。
要求如下:
定义四个类:雇员类、经理类、销售员类、销售经理类。
定义雇员类为基类,其中包含员工编号、姓名、月工资三个数据成员,
以及计算月工资、显示月工资的成员函数,构造函数、析构函数也应显式定义。
销售员、销售经理、经理的月收入按下述规定计算
经理:月工资固定8000元。
销售员:按当月销售额的5%提成。
销售经理:月固定工资4000元,并按部门月销售总额的0.5%提成。

类名:
Employee雇员、Manager公司经理、Saleperson销售员、Salemanager销售经理
变量:
number工号、name姓名、salary月工资、gdgz固定工资、sale销售总额
"""

class Employee:
    def __init__(self,num,name):
        self._num=num
        self._name=name
        self._salary=0

    def show(self):
        print('月工资为:',self._salary)

class Manager(Employee):
    def __init__(self,num,name):
        super().__init__(num,name)
        self._salary=8000

class Saleperson(Employee):
    def __init__(self,num,name,sale):
        super().__init__(num,name)
        self._sale=sale
        self._salary=self._sale*0.05

class SaleManager(Saleperson,Manager):
    def __init__(self,num,name,sale):
        super().__init__(num,name,sale)
        self._salary=4000+self._sale*0.005

def main():
    while 1:
        print('\n*****工资查询*****\n1.公司经理\n2.销售员\n3.销售经理\n0.退出\n')
        x=int(input('请输入查询的序号:'))
        if x==0:
            break
        num=int(input('工号:'))
        name=input('姓名:')
        if x==1:
            p=Manager(num,name)
        elif x==2:
            sale=int(input('销售总额:'))
            p=Saleperson(num,name,sale)
        elif x==3:
            sale=int(input('销售总额:'))
            p=SaleManager(num,name,sale)
        else:
            print('输入有误,请重新输入!\n')
            continue
        p.show()

if __name__=='__main__':
    main()

💡 Python中没有像C++中的虚继承,完全使用super来查找父类,实际在使用过程中发现,上面的钻石继承中,如果把class SaleManager(Saleperson,Manager):中的两个父类调换顺序,会出现TypeError: __init__() takes 3 positional arguments but 4 were given的错误。猜测原因是,Saleperson的属性比Manager多一个?查了很久没看到网上文献里面有带多个参数的钻石继承,给自己留个坑待埋。

如果觉得我的文章对您有用,请随意赞赏。您的支持将鼓励我继续创作!

一条评论

发表评论

邮箱地址不会被公开。