zl程序教程

您现在的位置是:首页 >  后端

当前栏目

【python 3】 面向对象

Python 面向对象
2023-09-14 09:13:57 时间

python 是面向对象的编程语言,面向对象编程(Object-oriented Programming,简称 OOP)是一种封装代码的方法,面向对象的编程可以更好的模拟真实世界里的事物(将其视为对象),并把描述特征的数据和代码框(函数)封装到一起。

面向对象的常用概念:

  • 类:可以理解成一个模板,通过它可以创造出无数个具体实例,可以复用且灵活性高。将类创建出实例的过程又称为类的实例化
  • 对象:类不能直接使用,通过类创建出来的实例(对象)才能使用,表示具体的事物
  • 属性:类中的所有变量称为属性
  • 方法:类中的所有函数称为方法,不过和函数不同的是,类方法至少要包含一个 self 参数,类方法无法单独使用,只能和类实例化出来的对象一起使用。

多个对象——>提取对象的特征和动作——>封装到一个类中

先有需求——>找出特征——>定义类

类:所有对象都要有共同的特征和共同的方法

  • 所有的类要求首字母大写,多个单词使用驼峰式命名
  • 所有的类都从 object 中继承过来的
class 类名(父类):
	属性:特性
		...
	方法:动作
		...

一、面向对象之属性

只要遇到 class,就在内存里边开辟空间,但可以不放东西。然后使用类来构建出一个个实际的对象。

# 定义类
class Phone():
    pass
# 用类来生成实际的对象,类名加() 就找到原来的类地址,创建一个和类相同的空间,地址不同
xiaohua = Phone()
print(xiaohua)
xiaoming = Phone()
print(xiaoming)
>>>
<__main__.Phone object at 0x00000197A0D6A208>
<__main__.Phone object at 0x00000197A0D6A128>
# 定义类
class Phone():
    brand = 'huawei'
# 直接用类里边的属性
xiaohua = Phone()
print(xiaohua.brand)
xiaoming = Phone()
print(xiaoming.brand)
>>>
huawei
huawei
# 定义类
class Phone():
    brand = 'huawei'
# 修改类中的属性
xiaohua = Phone()
xiaohua.brand = 'iPhone'
print(xiaohua.brand)
xiaoming = Phone()
print(xiaoming.brand)
>>>
iPhone
huawei

类属性:类自带的属性
对象属性:对象在调用了这个类生成对象以后,动态生成的自己的属性。

# 定义类和属性,只要定义类,就会产生一个空间,空间中有有属性,定义类只是得到一个初始的,但并不是实际的对象
class Student:
    # 类属性
    name = 'xiaoming'
    age = 3

# 使用类来构建一个对象,先定义类,再使用类
# 对象属性找的时候,先去这个对象空间里边找,找不到,就去类属性里边找
# 对象属性可以修改,但不会改动类的属性
xiaoming = Student()
# 赋值操作,在xiaoming的空间里新动态创建一个属性,这个属性只属于xiaoming这个空间,不属于类空间
# 属于这个对象自己的属性,称为 对象属性
xiaoming.age = 18
print(xiaoming.age)
>>>
18
xiaohua = Student()
print(xiaohua.name)
# 只要有赋值操作,就会在 xiaohua 的空间里边增加一个属性,原本是空的
xiaohua.name = 'xiaohua'
xiaohua.age = 1
print(xiaohua.name)
print(xiaohua.age)
>>>
xiaoming
xiaohua
1

怎么访问类属性:一定要用类名来访问

Student.name = 'Jack'
print(Student.name)
>>>
Jack

二、面向对象之方法

方法:动作部分

方法的分类:

  • 普通方法,属于类里边的方法,依赖于 self,
def 方法名(self[,参数])
	方法体...
  • 类方法(就属于这个类的方法)
  • 静态方法
  • 魔术方法

1)普通方法

class Phone:
    brand = 'xiaomi'
    price = 4999
    type = 'mate 80'
    # 普通方法
    def call(self):
        print('正在打电话...')
        # 当构造的对象叫 phone1 时,self指向 phone1 这个对象
        print('self:  ',self)  

phone1 = Phone()
print('phone1:',phone1) # 输出内存地址
phone1.call()
print('*********************************')
phone2 = Phone()
print('phone2:',phone2) # 输出内存地址
phone2.call()
>>>
phone1: <__main__.Phone object at 0x00000197A0D5C978>
正在打电话...
self:   <__main__.Phone object at 0x00000197A0D5C978>

*********************************

phone2: <__main__.Phone object at 0x00000197A0D63518>
正在打电话...
self:   <__main__.Phone object at 0x00000197A0D63518>

哪个对象调用 call,就会把这个对象作为参数传给 self 。就叫做自身。我自己调用 call,就把自己扔进去了。

class Phone:
    brand = 'xiaomi'
    price = 4999
    type = 'mate 80'
    # 普通方法
    def call(self):
        # 当构造的对象叫 phone1 时,self指向 phone1 这个对象
        print('note:  ',self.note)  

phone1 = Phone()
phone1.note = '我是 phone1 的 note'
phone1.call()  # 往括号里边传的就是 phone1
print('*********************************')
phone2 = Phone()
phone2.note = '我是 phone2 的 note'
phone2.call()
>>>
note:   我是 phone1 的 note
*********************************
note:   我是 phone2 的 note

self 的好处:谁调用,self 就是谁

这样操作可能出现的问题:不是每个对象的属性都是相同的,或者说有的对象没有某些属性。

下面这个例子,就是说不能保证每个被传进来的 self 都有 address_book.

class Phone:
    brand = 'xiaomi'
    price = 4999
    type = 'mate 80'
    # 普通方法
    def call(self):
        # 当构造的对象叫 phone1 时,self指向 phone1 这个对象  
        print('正在访问通讯录...')
        for person in self.address_book:
            print(person.items())

phone1 = Phone()
phone1.address_book = [{'15900889976':'xiaoming'},{'13457889999':'xiaohua'}]
phone1.call()  # 往括号里边传的就是 phone1
print('\n*********************************\n')
phone2 = Phone()
phone2.call()
>>>
正在访问通讯录...
dict_items([('15900889976', 'xiaoming')])
dict_items([('13457889999', 'xiaohua')])

*********************************

正在访问通讯录...
...
AttributeError: 'Phone' object has no attribute 'address_book'

解决上述问题:

魔术方法:只要用了类的名,系统默认就会执行

先定义一个类,会创建一个类,用类创建一个对象。

p = Phone()
# 1、先找有没有 Phone 这个空间
# 2、申请和 Phone 一样的空间,地址不同
# 3、回到类里边看,如果类里边提供了魔术方法__init__
#    如果没有,不用管,则执行将得到的内存空间给到 p
#    如果有,则进入 __init__,执行里边的动作,此时 init 里边的 self 是当这个对象 p 的地址,会把init里边的属性放到对象空间里边。之后就不能改类里边的属性了,可以改对象里边的属性。
# 4、执行完 init 之后,才给对象 p 赋值

init 作用:做一个标准化,保证每个通过这个类得到的对象,都有这些属性

class Phone:
    # 魔术方法之一(魔术方法:前后有 __ 的)
    def __init__(self):   # 初始化
        # self 是创建的对象
        # self. ... 就表示动态的给 self 空间中添加了两个属性
        self.brand = 'huawei'
        self.price = '4999'
        
    def call(self):
        # 当构造的对象叫 phone1 时,self指向 phone1 这个对象  
        print('正在访问通讯录...')
        for person in self.address_book:
            print(person.items())
p = Phone()

在 init 中直接传参:

class Phone:
    # 魔术方法之一(魔术方法:前后有 __ 的)
    def __init__(self, name, age):   
        self.name = name
        self.age = age
    def call(self):
        print('{}今年{}岁了!'.format(self.name, self.age))
p = Phone('xiaoming',22)
p.call()
>>>
xiaoming今年22岁了!

创建对象的时候,只要传值了,默认就会把值送到 init 里边。

class Cat:
    type = 'cat'
    # 通过__init__ 初始化特征
    def __init__(self, nickname, age, color):
        self.nickname = nickname
        self.age = age
        self.color = color
    # 动作:方法
    def eat(self, food):
        print('{}喜欢吃{}'.format(self.nickname,food))
    def catch_mouse(self, color, weight):
        print('{}抓了一只{}kg的老鼠,{}色的!'.format(self.nickname, weight, color))
    def sleep(self,hour):
        if hour<5:
            print('继续睡吧!')
        else:
            print('快起来抓老鼠!')
    def show(self):
        print('猫的详情:')
        print(self.nickname, self.color, self.age)
# 创建对象:利用 cat 这个类,构建一个真实的猫
cat1 = Cat('xiaoguai', 2, 'white')
# 调用这个类的方法
cat1.catch_mouse('黑','2')
cat1.eat('小金鱼')
cat1.sleep(5)
cat1.show()
>>>
xiaoguai抓了一只2kg的老鼠,黑色的!
xiaoguai喜欢吃小金鱼
快起来抓老鼠!
猫的详情:
xiaoguai white 2

普通方法的相互调用,要加 self

class Dog:
    
    def __init__(self,nickname):
        self.nickname = nickname
    def run(self):
        print('{}在院子里跑来跑去'.format(self.nickname))
    def eat(self):
        print('{}吃了很多'.format(self.nickname))
        self.run()
d = Dog('大黄')
d.eat()
>>>
大黄吃了很多
大黄在院子里跑来跑去

2)类方法

普通方法的 self 就是对象,要依靠对象来调用。

类方法直接用类就可以调用。

类方法,调用的时候往这里穿的不是对象,而是类。

特点:

  • 定义需要依赖装饰器 @classmethod
  • 类方法中的参数不是一个对象,而是当前的类
  • 类方法只能使用类属性
  • 类方法不能调用普通方法
  • 类方法,在对象还没有创建的时候,就可以访问,直接用类名调用类方法就可以了。

作用:

  • 因为只能访问类属性和类方法,可以在对象创建之前,如果需要完成一些功能,就可以放到类方法里边来。
class Dog:
    color = 'White'
    def __init__(self,nickname):
        self.nickname = nickname
    def run(self):
        print('{}在院子里跑来跑去'.format(self.nickname))
    @classmethod
    def test(cls):  
        print(cls) # <class '__main__.Dog'>
# 类方法的调用
Dog.test()
>>>
<class '__main__.Dog'>

类方法不能调用有self的方法,因为有self的方法的调用都要依赖于self。

类方法的作用例子:

# 非私有属性,可以在外边直接访问到
class Person:
    age = 18
    def show(self):
        print('---------->',Person.age)
        print('---------->',self.age)
    @classmethod
    def test(cls):
        print('---------->类方法')
Person.test()
Person.age
>>>
---------->类方法
18

私有类:只能在类里边改这个属性,在外边不能访问以及修改

怎么操作呢?只能依赖于类方法对其进行修改

class Person:
    __age = 18
    def show(self):
        print('---------->',Person.age)
    
    @classmethod
    def test(cls):
        print('---------->类方法')
        cls.__age = 20
        print('修改后的年龄是:', cls.__age)
    
# 不创建对象,就想要改属性的需求
Person.test()
>>>
---------->类方法
修改后的年龄是: 20

3)静态方法

静态方法:类似于类方法

  • 需要装饰器 @staticmethod
  • 没有参数
  • 只能访问类的属性和方法,无法访问对象的属性和方法
  • 加载时机和类方法相同
class Person:
    __age = 18
    def __init__(self, name):
        self.name = name
        
    def show(self):
        print('---------->',Person.age)

    @staticmethod
    def test():
        print('---------->静态方法')
        print(Person.__age)
Person.test()
>>>
---------->静态方法
18

类方法和静态方法的不同:

  • 装饰器不同
  • 参数不同,类有参数,静态方法没有参数

相同之处:

  • 都只能访问类的属性和方法
  • 都可以通过类名调用访问
  • 都可以在创建对象之前来使用,因为都是不依赖于对象的。

普通方法和上述两者的不同:

  • 普通方法没有装饰器
  • 普通方法依赖于对象,因为每个普通方法都有一个self,而self表示对象本身
  • 只有创建了对象,才可以调用普通方法

4)魔术方法

魔术方法就是一个类/对象中的方法,和普通方法唯一的不同是,普通方法需要调用,而魔术方法是在特定时刻自动触发。只要你写了这个魔术方法,不用调也能自动触发。

1、_ init _:初始化魔术方法

触发时机:初始化对象时触发,不是实例化触发,但和实例化在一个操作中

2、_ new _:实例化的魔术方法
触发时机:在实例化时,进行触发

3、_ call _:将对象当成函数调用的魔术方法
触发时机:将对象当做函数调用时触发

4、_ del :delete的缩写,析构魔术方法
触发时机:当对象没引用的时候被触发,也就是没有任何变量引用的时候。
5、
_ str_ :打印对象名
触发时机:打印对象名的时候,自动触发,调用__str
_ 里边的内容。

一定要在这个方法中加return,return 后面的内容就是打印对象看到的内容

单纯打印对象的名称,对开发者没有太大的意义,但使用__str__ 就可以直接打印变量名。

class Person:
    def __init__(self, name):
        self.name = name
p = Person('nana')
print(p)
>>>
<__main__.Person object at 0x00000197A0D96EF0>
class Person:
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return '姓名是:'+self.name
p = Person('nana')
print(p)
>>>
姓名是:nana

三、私有化

class Student:
    def __init__(self, name, age):
        self.__score = 59
        self.name = name
        self.age = age
    def __str__(self):
        return '姓名:{}, 年龄:{}, 分数:{}'.format(self.name, self.age, self.__score)
student = Student('nana',18)
print(student)
>>>
姓名:nana, 年龄:18, 分数:59

能改吗?下面没有改成功

student.age = 21
student.__score = 95
print(student)
>>>
姓名:nana, 年龄:21, 分数:59

面向对象有一种封装操作:

  • 私有化属性
  • 定义共有的 set 和 get 方法

为什么要私有化:

  • 不希望外界来改我的属性
  • 也可以修改,通过 def setXXX(self,XXX)来修改,
  • 还可以通过范围限制修改。不希望外界用不合适的值进行修改,就可以在 set 里边判断,是否符合我的范围,如果在内,则可以赋值成功。
  • 如果想获取具体的某一个属性,就可以使用 getXXX()

因为外界改不了,所以如果要改的话,要向外界暴露一个公有的东西,set 和 get。

  • set :赋值,也就是将外界给的值给到私有的属性
  • get:取值
class Student:
	def __init__():
		...
	# setAge() 给私有属性赋新值
	def setAge(self,age):
		self.__age = age
	# getAge() 可以在外边直接调用 p.getAge() 来访问
	def getAge(self):
		return self.__age
nana = Student()
nana.SetAge(21)
nana.getAge()
class Student:
    def __init__(self, name, age):
        self.__score = 59
        self.__name = name
        self.__age = age
    def setName(self,name):
        if len(name)>6:
            self.__name = name
        else:
            print('姓名长度必须大于6位!')
    def setAge(self,age):
        if age>0 and age<100:
            self.__age = age
        else:
            print('年龄不在规定范围内!')
    def getAge(self):
        return self.__age
    def getName(self):
        return self.__name
    def setScore(self,score):
        if score<100 and score>0:
            self.__score = score
        else:
            print('成绩不符合规范,请重新输入')
    def getScore(self):
        return self.__score
    def __str__(self):
        return '姓名:{}, 年龄:{}, 分数:{}'.format(self.__name, self.__age, self.__score)
nana = Student('nana',18)
print(nana)

nana.setAge(21)
print(nana)
print('\n---------------------------\n')
nana = Student('nana',25)
print(nana)
nana.setScore(95)
print(nana)
print('\n---------------------------\n')
print(nana.getName())
print(nana.getAge())
print(nana.getScore())
>>>

姓名:nana, 年龄:18, 分数:59
姓名:nana, 年龄:21, 分数:59

---------------------------

姓名:nana, 年龄:25, 分数:59
姓名:nana, 年龄:25, 分数:95

---------------------------

nana
25
95

只要加了__name,底层就会给你改名成 _类名__name。

四、装饰器

在开发中的私有化处理,装饰器

给setAge和getAge加一层装饰器,让外界用起来和没有私有化是一样的,但功能还能和私有化一样,加一些限制。

class Student:
    def __init__(self, name, age):
        self.__score = 59
        self.__name = name
        self.__age = age
    @property
    def age(self):
        return self.__age
    @age.setter
    def age(self, age):
        if age>0 and age<100:
            self.__age = age
        else:        
            print('年龄不在规定范围内!')
     def __str__(self):
         return '姓名:{}, 年龄:{}, 分数:{}'.format(self.__name, self.__age, self.__score)
nana = Student('nana',18)
nana.age = 130
print(nana.age)
print(nana)
>>>
年龄不在规定范围内!
18
姓名:nana, 年龄:18, 分数:59

五、继承

公路(Road):
	属性:公路名称,公路长度
车(Car):
	属性:车名,时速
	方法:1、求车名在哪条公路上以多少的时速行驶了多长时间
		  2、初始化车辆属性信息
		  3、打印对象显示车的属性信息
import random
class Road:
    def __init__(self, name, len):
        self.name = name
        self.len = len
        
class Car:
    def __init__(self,brand, speed):
        self.brand = brand
        self.speed = speed
    def get_time(self, road):
        ran_time = random.randint(1,10)
        msg = '{}品牌的车在{}上以{}速度行驶{}个小时!'.format(self.brand,road.name,self.speed,ran_time)
        print(msg)
    def __str__(self):
        return '{}品牌的车,速度:{}'.format(self.brand,self.speed)
#创建实例化对象
r = Road('京藏高速',12000)
print(r.name)
audi = Car('Audi',120)
print(audi)
audi.get_time(r)
>>>
京藏高速
Audi品牌的车,速度:120
Audi品牌的车在京藏高速上以120速度行驶2个小时!

1)has a 关系:

has a:一个类里边有另一个类,不完全是继承关系,但是是一种包含关系。

# 创建三个类,则系统开辟三个空间,分别存这三个类的信息
# 然后调用的时候,就是创建三个对象
# student对象,需要name,computer
# 这里的computer也是一种类型,不过是自定义类型
# list、int 等都是系统提供的类型
# 
class Computer:
	pass
class Book:
	pass
class Student:
	pass
class Computer:
    def __init__(self, brand, type, color):
        self.brand = brand
        self.type = type
        self.color = color
    def online(self):
        print('正在使用电脑上网...')
    def __str__(self):
        return self.brand + '---' + self.type + '---' + self.color
        
class Book:
    def __init__(self, bname, author, number):
        self.bname = bname
        self.author = author
        self.number = number
    def __str__(self):
        return self.name + '---' + self.author + '---' + str(self.number)

class Student:  # has a 
    def __init__(self,name,computer,book):
        self.name = name
        self.computer = computer
        self.books=[]
        self.books.append(book)
    def borrow_book(self, book):
        for book1 in self.books:
            if book1.bname == book.bname:
                print('已经借过了')
                break
        else:
            # 将这本书加到列表中
            self.books.append(book)
            print('添加成功!')
    def show_book(self):
        for book in self.books:
            print(book.bname)
    def __str__(self):
        return self.name + '---' + str(self.computer) + '---' + str(self.books)

# 创建对象
computer = Computer('mac','mac pro 2018','深灰色')
book = Book('盗墓笔记','南派三叔',10)
student = Student('nana',computer,book)
print(student)
book1 = Book('鬼吹灯','天下霸唱',8)
student.show_book()
student.borrow_book(book1)
print('--------------')
student.show_book()
>>>
nana---mac---mac pro 2018---深灰色---[<__main__.Book object at 0x000001F0F2343668>]
盗墓笔记
添加成功!
--------------
盗墓笔记
鬼吹灯

知识点:

  • 理解 has a,一个类里边使用了另外一种自定义类型
    • student 里边使用了 computer 和 book
  • 类型:
    • 系统类型:str int float list dict tuple set 等
    • 自定义类型:自定义的类,都可以看做一种类型
      s = Student() s是Student类的对象

2)is a 的关系:

如果 A 是 B,那么 B 就是 A 的基类,基类就是父类。

为什么要层层继承:

很多类中的方法是重复的,所以把每个类中间都具备的共同特征或共同属性,放到一个大类里边,其他的类直接继承来用就好啦。

# 基类:父类 base class
class Student:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def eat(self):
        print(self.name + '正在吃饭!')
    def run(self):
        print(self.name + '正在跑步!')
class Employee:
    def __init__(self, name, age):
        self.name = name
        self.age = age
class Doctor:
    pass

使用继承的方法:把父类里边的东西都继承过来了,调用的时候是跑到父类里边去调用的。

所有的类默认继承的就是 Object,是所有类的父类。
在这里插入图片描述

class 子类(父类):
	...

什么时候需要继承:

  • 多个类属于相同的大类
  • 多个类有相同的代码,冗余,代码可读性不高

如何继承:

  • 将相同的代码提取出来,得到一个基类
  • 让子类继承基类 class Student(Person): ...

特点:

  • 如果类中不定义 init,去父类找
  • 如果需要定义自己的 init,需要在子类的 init 中调用父类的
  • 如何调用父类的 init:
    • super().init(参数)
    • super(类名,self).init(参数)
  • 如果父类的方法无法满足子类的需求,子类中会重写一个同名方法,这个方法会把父类的方法覆盖。也就是如果父类有 eat,子类也有eat,默认先找自己的,再找父类,会把父类的覆盖。
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def eat(self):
        print(self.name + '正在吃饭!')
    def run(self):
        print(self.name + '正在跑步!')
class Students(Person):
    pass
class Employee(Person):
    pass
class Dctor(Person):
    pass

s = Students('nana',14)  # 执行步骤,先调 __new__,产生新空间传给 __init__,student什么都没有,就会到其父类里边找
s.run()
>>>
nana正在跑步!

子类定义 init:都要把父类的 init 调用一下才可以

所有的类传参数都是传到 init 去了,所以,super 传参也就传到 init去了。

super其实是一个类,既然是个类,那么就要传参数,往init传参数。

调用的目的:保证把共同的特征拿过来。

# super() 表示父类对象
super().__init__()

无参数情况下定义 init:

class Person:
    def __init__(self):
        self.name = 'nana'
        self.age = 14
    def eat(self):
        print(self.name + '正在吃饭!')
    def run(self):
        print(self.name + '正在跑步!')
class Students(Person):
    def __init__(self):
        print('-----> student 的 init')
        super().__init__()
s = Students()  # 执行步骤,先调 __new__,产生新空间传给 __init__,student什么都没有,就会到其父类里边找
s.run()
>>>
-----> student 的 init
nana正在跑步!

有参数情况下定义 init:

为什么要在每个子类定义 init:因为每个子类都会有他的特有属性和方法。

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def eat(self):
        print(self.name + '正在吃饭!')
    def run(self):
        print(self.name + '正在跑步!')
class Students(Person):
    def __init__(self, name, age):
        print('-----> student 的init')
        super().__init__(name, age)
class Employee(Person):
    pass
class Dctor(Person):
    pass

s = Students('Jack',18)  # 执行步骤,先调 __new__,产生新空间传给 __init__,student什么都没有,就会到其父类里边找
s.run()
e = Employee('lily',13)
e.run()
>>>
-----> student 的init
Jack正在跑步!
lily正在跑步!

并非所有类的属性都是共有的,类都会有自己特有的东西,所以,需要定义个体的不同之处:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def eat(self):
        print(self.name + '正在吃饭!')
    def run(self):
        print(self.name + '正在跑步!')
        
class Students(Person):
    def __init__(self, name, age, classes):
        super().__init__(name, age)  # 利用 super 把父类有的属性拿过来
        self.classes = classes           # 自己定义的属性
    def study(self, course):
        print('{}正在学{}'.format(self.name, course))
    def eat(self, food):
#         super().eat()
        print('{}正在吃饭,喜欢吃{}'.format(self.name, food))
        
class Employee(Person):
    def __init__(self, name, age, salary, manager):
        super().__init__(name, age)
        self.salary = salary
        self.manager = manager
    
class Doctor(Person):
    def __init__(self, name, age, patients):
        super().__init__(name, age)
#         super(Doctor, self).__init__(name, age) # 和上面等价,不过底层会做一个 isinstance 的判断,判断传进来的是不是 Doctor
        self.patients = patients

s = Students('Jack',18, '1611')  # 执行步骤,先调 __new__,产生新空间传给 __init__,student什么都没有,就会到其父类里边找
s.run()
s.study('python基础')
s.eat('红烧肉')
e = Employee('lily',23, 10000, 'king')
e.run()
list1 = ['tom','jack','json','lili']
d = Doctor('lucy', 30, list1)
d.run()
>>>
Jack正在跑步!
Jack正在学python基础
Jack正在吃饭,喜欢吃红烧肉
lily正在跑步!
lucy正在跑步!

练习:工资管理程序

编写一个工资管理程序,管理以下四类人: worker, salesman, manager, salemanager。
所有员工都具有工号,姓名,工资等属性,有设置姓名,获取姓名,获取员工号,计算工资等方法。
1)worker:具有工作小时数和时薪的属性,工资 = 小时*时薪
2)salesman:具有销售额和提成比例的属性,工资 = 销售额*提成
3)manager:固定月薪
4)salasmanager:工资 = 销售额*提成比例*月薪
请完成以下功能:
1)添加所有类型的人员
2)计算月薪
3)显示所有人工资
class Person:
    def __init__(self, no, name, salary):
        self.no = no
        self.name = name
        self.salary = salary
    def __str__(self):
        msg = '工号:{}, 姓名:{}, 本月工资:{}'.format(self.no, self.name, self.salary)
        return msg
    def getSalary(self):
        return self.salary
class Worker(Person):
    def __init__(self, no, name, salary, hours, per_hour_money):
        super().__init__(no, name, salary)
        self.hours = hours
        self.per_hour_money = per_hour_money
    def getSalary(self):
        money = self.hours*self.per_hour_money
        self.salary += money
        return self.salary
class Salesman(Person):
    def __init__(self, no, name, salary, salemoney, percent):
        super().__init__(no, name, salary)
        self.salemoney = salemoney
        self.percent = percent
    def getSalary(self):
        money = self.salemoney*self.percent
        self.salary += money
        return self.salary
# 创建对象
worker = Worker('001','king',2000,160,50)
S = worker.getSalary()
print('月薪是:',S)
print(worker)
saler = Salesman('002','lucy',5000,5000000,0.003)
s = saler.getSalary()
print('月薪是:',s)
print(saler)
>>>
月薪是: 10000
工号:001, 姓名:king, 本月工资:10000
月薪是: 20000.0
工号:002, 姓名:lucy, 本月工资:20000.0

有重名的方法,后面的会把前面的覆盖掉:

class Person:
    def __init__(self, name):
        self.name = name
    def eat(self):
        print('--------eat1')
    def eat(self,food):
        print('--------eat',food)
p = Person()
p.eat()
>>>
TypeError: eat() missing 1 required positional argument: 'food'

p = Person('jack')
p.eat('红烧肉')
>>>
--------eat 红烧肉

python 允许多继承:

class A:
    def test(self):
        print('AAAAAAAAA')
class B:
    def test1(self):
        print('BBBBBBBBB')
class C(A,B):
    def test2(self):
        print('CCCCCCCCC')
c = C()
c.test1()
c.test()
>>>
BBBBBBBBB
AAAAAAAAA

多继承的搜索顺序:

class Base:
    def test(self):
        print('-------Base---------')
class A(Base):
    def test(self):
        print('AAAAAAAAAAAAAAAAAAAA')
class B(Base):
    def test(self):
        print('BBBBBBBBBBBBBBBBBBBB')
class C(Base):
    def test(self):
        print('CCCCCCCCCCCCCCCCCCCC')
class D(A, B, C):
    pass
d = D()
d.test()
import inspect
print(inspect.getmro(D))
print(D.__mro__) # 获取搜索顺序方法二
>>>
AAAAAAAAAAAAAAAAAAAA
(<class '__main__.D'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.Base'>, <class 'object'>)

python 允许多继承:

def 子类(父类1,父类2:
	pass

父类中有相同的方法时,搜索顺序:从左至右,深度优先

六、多态

python 类似于多态,但是是依赖于 isinstance 来判断的。

isinstance(obj,): 判断obj是不是该类的对象或该类子类的对象
class Person:
    def __init__(self,name):
        self.name = name
    def feed_pet(self, pet):
        if isinstance(pet, Pet):  
            print('{}喜欢养宠物:{},昵称是:{}'.format(self.name,pet.role, pet.nickname))
        else:
            print('不是宠物,不能养!')
        
class Pet:
    role = 'Pet'
    def __init__(self, nickname, age):
        self.nickname = nickname
        self.age = age
    def show(self):
        print('昵称:{},年龄:{}'.format(self.nickname, self.age))
class Cat(Pet):
    role = 'Cat'
    def catch_mouse(self):
        print('抓老鼠...')
class Dog(Pet):
    role = 'Dog'
    def whatch_house(self):
        print('看家高手...')
class Tiger:
    def eat(self):
        print('太可怕了...')
# 创建对象
cat = Cat('huahua', 2)
dog = Dog('dahuang', 4)
tiger = Tiger()
person = Person('nana')
person.feed_pet(cat)
person.feed_pet(tiger)
>>>
nana喜欢养宠物:Cat,昵称是:huahua
不是宠物,不能养!

七、单例模式

开发模式:单例模式

单个对象就叫单例模式

class Student:
    pass
s = Student()
s1 = Student()
s2 = Student()

上面这种创建对象的模式,会导致每次调用这个类,系统都会给这个对象分配一个空间,多个地址。

单例模式,开发过程中,一个实例就搞定的,不需要每次实例化的时候都开辟一个空间。也就是每次调用这个类的时候,都指向同一个地址,不会占用很多空间。

调用的时候底层的操作:

  • 先找 new,帮我们产生内存地址,产生后赋值给 __instance,返回给 init
  • 再找 init,如果找不到,就找父类的init
  • 最后赋值地址给对象
class Singleton:
	# __instance 就是保存变量空间的私有变量
    # 私有化
    # 保证单例的地址都存在在这里,别人也没法改
    __instance = None
    # __new__ 是开辟空间的方法,所以要单例化,就要重写这个方法
    # 重写父类,把__new__修改了
    def __new__(cls):
        print('----->instance:', cls.__instance)
        if cls.__instance is None:
            print('------->1')
            cls.__instance = object.__new__(cls)
            return cls.__instance
        else:
            print('------->2')
            return cls.__instance
# 调实例化对象,先判断__instance是否为 None,如果为 None,则调用父类的 new 给分配空间,且修改了__instance
s = Singleton()
# 再次实例化对象,__instance 已经不是 None了,所有会走 else 分支
s1 = Singleton()
print(s)
print(s1)
>>>
----->instance: None
------->1
----->instance: <__main__.Singleton object at 0x000001F0F2746390>
------->2
<__main__.Singleton object at 0x000001F0F2746390>
<__main__.Singleton object at 0x000001F0F2746390>