Python基础知识 2022-11-16 ★ 小结 97-113 方法_多态_继承

方法没有重载_方法的动态性

方法没有重载

其它语言中,可以定义多个重名的方法,只要保证方法签名唯一即可,方法签名包括:方法名、参数数量、参数类型
Python中,方法的参数没有类型,参数的数量也可以由可变参数控制,因此没有python中的方法没有重载
定义一个方法就有多种调用方法,相当于实现了其他语言中的重载

如果定义了多个重名的参数,只有最后一个有效

方法的动态性

Python是动态语言
可以动态地为类添加新的方法,或者动态的修改类的方法

#测试方法的动态性  
  
class Person:  
    def work(self):  
        print("努力上班")  
  
def play_game(s):  
    print("{0}在玩游戏".format(s))  
  
def work2(s):  
    print("好好工作")  
  
Person.play = play_game # 给类对象增加新的方法  
p = Person()  
p.work()  
p.play() # Person.play(p)  
  
Person.work = work2 # 修改类对象原有的方法  
p.work()

我的小结:只要方法不加圆括号,就是方法这个对象本身,可以赋值给类对象。类对象中方法的添加和修改都可以这样完成。新增的方法不在类对象内部定义。
我的小结:类对象中方法的修改Person.work = work2
我的小结:类对象中方法的新增Person.work = work

私有属性和私有方法(实现封装)

私有属性

Python对于类的成员没有严格的访问控制限制,区别于其他面向对象的语言
私有属性和私有方法的要点:

  1. 通常,两个下获悉开头的属性是私有的privat,其它为公共的public
  2. 类内部可以访问私有属性(方法)
  3. 类外部不能直接访问思欧属性(方法)
  4. 类外部可以通过_类名__私有属性(方法)名访问私有属性(方法)

注意:方法本质上也是属性,只不过可以通过圆括号()执行

#测试私有属性  
  
class Employee:  
    def __init__(self,name,age):  
        self.name = name  
        self.__age = age  
  
e = Employee("gaoqi",18)  
  
print(e.name)  
print(e._Employee__age)  
print(dir(e))

注意:
调用私有属性的时候,类名前面是一个下划线,不是两个,私有属性前才是两个下划线

私有方法

#测试私有属性  
  
class Employee:  
    __company = "一家亲"  
  
    def __init__(self,name,age):  
        self.name = name  
        self.__age = age # 私有属性  
    def __work(self): # 私有方法(方法也是属性)  
        print("好好工作")  
        print('age:{0}'.format(self.__age))  
        print(Employee.__company)  
  
e = Employee("gaoqi",18)  
  
print(e.name)  
print(e._Employee__age)  
print(dir(e))  
e._Employee__work()  
  
print(Employee._Employee.__company) # 注意这里不要漏掉`_Employee`

注意:类对象本身的类属性也可以私有,调用的时候是类名称._类名称.__私有类属性名称

@property装饰器_get和set方法

可以将一个方法的调用变成属性调用

#@property装饰器的用法

class Employee:

    def __init__(self,name,salary):
        self.__name = name
        self.__salary = salary

    @property
    def salary(self):
        return self.__salary

    @salary.setter # 针对salary属性的一个设置
    def salary(self, a):
        if 1000 < a < 50000:
            self.__salary = a
        else:
            print("录入错误")

    # def get_salary(self):
    #     return self.__salary
    #
    # def set_salary(self, salary):
    #     if 1000 < salary < 50000:
    #         self.__salary = salary
    #     else:
    #         print("录入错误")



emp1 = Employee("gaoqi", 3000)
# print(emp1.salary)
emp1.salary = -2000
# print(emp1.salary)
print(emp1.salary)

❓一整个都不是太理解

面向对象的三大特征说明(封装、继承、多态)

三大特征:封装、继承、多态

封装(隐藏)

隐藏什么:隐藏对象的属性和实现细节
只对外提供必要的方法,相当于把细节封装起来,只对外暴露“相关调用方法”
举例:
要创建一个手机,但不需要知道摄像头是怎么连接的,只需要知道怎么使用
实现方式:私有属性、私有方法
Python追求简洁的语法,没有严格的语法级别的“访问控制符”,更多依靠程序员自觉实现

继承

让子类具有父类的特性,提高了代码的重用性
设计上,是一种增量进化,原有父类设计不变,可以增加新功能,或改进已有算法

多态

多态是指同一个方法调用,由于对象不同,会产生不同的行为
举例:生活中这样的例子,同样是休息这个方法,每个人调用休息的方式不同,有人睡觉、有人玩游戏、有人小代码…

我的小结:封装,减小用户干扰;继承,提高代码重用性、方便增加和改进;多态,同人不同用法不同结果

继承

代码复用的重要手段
父类,基类
子类,派生类
类比:动物的界门纲目科属种

语法格式

Python支持多重继承,一个子类可以继承多个父类
语法:

class 子类类名(父类1[, 父类2...])
	类体

如果在类定义中没有指定父类,就默认父类是object类,这是所有类的父类,其中定义了一些所有类共有的默认实现,比如__new__()

定义子类时,必须(逻辑上必须,语法上不一定)在其构造函数中调用父类的构造函数
格式:

#测试继承的基本使用  
  
class Person:  
    def __init__(self, name, age):  
        self.name = name  
        self.age = age  
  
    def say_age(self):  
        print("donno age")  
  
class Student(Person):  
  
    def __init__(self,name,age,score):  
        Person.__init__(self,name,age) # 必须显式地调用父类初始化方法,否则不会被调用★★★  
        self.score = score  
  
  
# Student继承了Person,Person继承了Object  
# print(Student.mro()) # 看继承关系  
  
s = Student('gaoqi',18,60)  
# s.say_age()  
# print(s.name)  
# print(s.age)  
# print(dir(s))

方法的重写

类成员的继承和重写

子类继承父类除了构造器之外的所有成员
方法重写:子类可以重新定义父类中的方法,会覆盖父类中的方法

# 测试方法的重写  
  
class Person:  
    def __init__(self, name, age):  
        self.name = name  
        self.__age = age  
  
    def say_age(self):  
        print("我的年龄是{0}".format(self.__age))  
  
    def say_introduce(self):  
        print("我的名字是{0}".format(self.name))  
  
class Student(Person):  
  
    def __int__(self,name,age,score):  
        Person.__init__(self,name,age) # 必须显式地调用父类初始化方法,否则不会被调用★★★  
        self.score = score  
  
    def say_introduce(self):  
        '''重写了父类的方法'''  
        print("报告老师,我的名字是{0}".format(self.name))  
  
s=Student("gaoqi",19)  
s.say_age()  
s.say_introduce()

❓仍未解决父类中参数数量限制了子类参数的问题

object根类_dir()

查看类的继承层次结构

类的方法mro()
类的属性__mro__
![[Pasted image 20221116144342.png]]

Object根类

是所有类的父类,所有类都有object的属性和方法
查看object的属性:

obj = object()
print(dir(obj))

方法也是属性,只不过类型是“method”,狭义的属性通常是整型、字符串等这种类型

重写__str__()方法

object中str方法,用于返回一个对于“对象的描述”,对应于内置函数str(),经常用语print()方法,帮我们查看对象信息
__str__()可以重写

#测试重写object中的__str__()  
  
class Person:  
    def __init__(self,name):  
        self.name = name  
    def __str__(self):  
        return "名字是{0}".format(self.name)  
  
p = Person("gaoqi")  
print(p)

❓重写来有什么用?重写相当于是重新定义整个方法吗?还是增加了功能(类似于续写)?

多重继承

Python支持多重继承,一个子类可以有多个直接的父类,
优点:具备了多个父类的特点
缺点:类的整体层次会很复杂,不利于维护
因此尽量避免使用

# 父类继承  
  
class A:  
    def aa(self):  
        print('aa')  
    def say(self):  
        print("say AAA!")  
  
class B:  
    def bb(self):  
        print('bb')  
    def say(self):  
        print("say BBB!")  
  
class C(B,A):  
    def cc(self):  
        print('cc')  
  
c = C()  
print(C.mro()) # 打印类的层次结构  
c.say() # 解释器寻找方法是从左到右,此时会执行B类中的say()

结果:
cc
bb
aa
我的小结:继承后,会先执行本类体内的函数体,然后才会依次执行父类列表中的函数体

mro()

Python支持多继承,如果父类中有相同名字的方法,在子类没有指定父类名称时候,解释器将从左向右按顺序搜索

# 父类继承  
  
class A:  
    def aa(self):  
        print('aa')  
    def say(self):  
        print("say AAA!")  
  
class B:  
    def bb(self):  
        print('bb')  
    def say(self):  
        print("say BBB!")  
  
class C(B,A):  
    def cc(self):  
        print('cc')  
  
c = C()  
print(C.mro()) # 打印类的层次结构  
c.say() # 解释器寻找方法是从左到右,此时会执行B类中的say()

super()获得父类的定义

通过super()获得父类定义,而不是父类的对象
定义姑且可以理解成“代码”

# 测试super(),代表父类的定义,而不是父类的对象  
  
class A:  
  
    def say(self):  
        print("A:",self)  
  
class B(A):  
  
    def say(self):  
        # A.say(self)  
        super().say()  
        print("B:",self)  
  
B().say()

多态

多态,polymorphism,同一个方法,被不同对象调用,产生不一样的行为
注意:

  1. 是方法的多态,而不是属性,属性没有多态
  2. 多态必要条件:继承、重写
#polymorphism  
  
class Man:  
    def eat(self):  
        print("吃饭!")  
  
class Chinese(Man):  
    def eat(self):  
        print("中国人吃饭!")  
  
class English(Man):  
    def eat(self):  
        print("英国人吃饭!")  
  
class India(Man):  
    def eat(self):  
        print("印度人吃饭!")  
  
def manEat(m):  
    if isinstance(m,Man):  
        m.eat()  
    else:  
        print("不能吃饭")  
  
a = Chinese()  
manEat(Chinese())  
manEat(India())  
manEat(a)

结果:
中国人吃饭!
印度人吃饭!
中国人吃饭!

特殊方法和运算符重载

Python的运算符实际上是通过调用对象的特殊方法实现的

a = 20
b = 30
c = a+b
d = a.__add__(b)
print("c:",c)
print("d:",d)

每个运算符实际上对应的方法

所以所有的运算符号都是对象
特殊的方法

测验

# 自定义的person相加相乘
# 测试运算符的重载  
  
class Person:  
    def __init__(self,name):  
        self.name = name  
    def __add__(self, other):  
        if isinstance(other,Person):  
            return "{0}--{1}".format(self.name, other.name)  
        else:  
            return "不是同类对象,不能相加"  
    def __mul__(self, other):  
        if isinstance(other, int):  
            return self.name*other  
        else:  
            return "不是同类不能相乘"  
  
p1 = Person('gao')  
p2 = Person("qi")  
x = p1+p2  
y = p1*30  
print(x)  
print(y)

特殊属性

Python对象包含了很多双下划线开始和结束的属性,是特殊属性,有特殊用法

# 父类继承  
  
class A:  
    def aa(self):  
        print('aa')  
    def say(self):  
        print("say AAA!")  
  
class B:  
    def bb(self):  
        print('bb')  
    def say(self):  
        print("say BBB!")  
  
class C(B,A):  
    def __init__(self,nn):  
        self.nn = nn  
  
    def cc(self):  
        print('cc')  
  
c = C(3)  
  
print(dir(c)) # 对象的属性列表  
print(c.__dict__) # 对象的属性字典  
print(c.__class__) # 对象的类  
print(C.__bases__) # 类的基类元组  
print(C.__base__) # 类的基类(从左到右第一个)  
print(C.mro()) # 类的层次结构  
print(A.__subclasses__()) # 子类列表

对象的浅拷贝和深拷贝_内存分析

对象的浅拷贝和深拷贝

变量的赋值操作

只是形成两个变量,实际还是指向同一个对象

浅拷贝

Python的拷贝一般都是浅拷贝,拷贝时,对象包含的子对象不拷贝,因此,源对象和拷贝对象会引用同一个子对象(脐带还在)

深拷贝

使用copy 模块中的deepcopy函数,递归拷贝对象中包含的子对象,源对象和拷贝对象所有的子对象也不同(新建了脐带)

# 测试对象的浅拷贝和深拷贝、  
import copy  
class MobilePhone:  
    def __init__(self, cpu, screen):  
        self.cpu = cpu  
        self.screen = screen  
  
class CPU:  
    def calculate(self):  
        print("算你个12345")  
        print("cpu对象:",self)  
  
class Screen:  
    def show(self):  
        print("显示一个好看的画面")  
        print("screen对象:",self)  
  
# 测试变量赋值  
# m1 = MobilePhone()  
# m2 = m1  
# print(m1)  
# print(m2)  
c1 = CPU()  
c2 = c1  
print(c1)  
print(c2)  
  
# 浅复制  
s1 = Screen()  
m1 = MobilePhone(c1,s1)  
m2 = copy.copy(m1)  
print(m1, m1.cpu, m1.screen)  
print(m2, m2.cpu, m2.screen)  
  
  
#深复制  
  
m3 = copy.deepcopy(m1)  
print(m1, m1.cpu, m1.screen)  
print(m3, m3.cpu, m3.screen)

组合

这类似于继承,都可以起到代码复用的作用
“is-a”,用继承:狗狗是动物
“has-a”,用组合:手机有CPU

测试:

#测试组合  
  
# 继承实现代码的复用  
class A1:  
    def say_a1(self):  
        print("a1")  
  
class B1(A1):  
    pass  
  
b1 = B1()  
b1.say_a1()  
  
# 组合实现代码的复用  
class A2:  
    def say_a2(self):  
        print("a2")  
  
class B2:  
    def __init__(self, a):  
        self.a = a  
a2 = A2()  
b2 = B2(a2)  
b2.a.say_a2()

我的小结:继承,是在定义类的时候发生;组合,是在调用的时候发生

# 测试has-a关系,使用组合  
# 测试对象的浅拷贝和深拷贝、  
import copy  
class MobilePhone:  
    def __init__(self, cpu, screen):  
        self.cpu = cpu  
        self.screen = screen  
  
class CPU:  
    def calculate(self):  
        print("算你个12345")  
        print("cpu对象:",self)  
  
class Screen:  
    def show(self):  
        print("显示一个好看的画面")  
        print("screen对象:",self)  
  
m = MobilePhone(CPU(), Screen())  
m.cpu.calculate()  
m.screen.show()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值