python-第八节-面向对象进阶

Python中方法没有重载
其他语言中,可以定义多个重名的方法,只要保证方法签名唯一即可。方法签名包含3个部分:方法名、参数数量、参数类型
也就是说以上三个部分的异同决定方法的异同,调用方法时通过传参的类型和数量等去调用对应的方法

在python中,方法的参数没有类型(调用时确定参数的类型),参数的数量也可以由可变参数控制。因此,python中是没有方法的重载的。定义一个方法即可有多种调用方式,相当于实现了重载的功能。如果我们在类中定义了多个重名的方法,只有最后一个方法有效

故不建议使用重名的方法!Python中方法没有重载。

测试方法的有效性

class Person:
 def Sayhi(self):
 print(“hello”)
 def Sayhi(self,name):
 print(“你好,{0}”.format(name))
 p1 = Person()

p1.Sayhi() 报错,因为默认使用的是最后定义的同名的方法

p1.Sayhi(“xx”) # 你好,xx 使用的是最后定义的同名方法

测试方法的动态性

class Person:
 def work(self):
 print(“努力上班!”)
 def play_game(s):
 print("{0}在玩游戏".format(s))
 p1= Person()
 p1.work()

p1.play_game 运行报错 因为类中没有这个方法

Person.play = play_game
p2 = Person()
p2.work()
p2.play() # 这里相当于在Person类中添加play属性,属性赋值为play_game方法的地址值
#就相当于Person.play_game(p2),传进去p2的地址值

私有属性和私有方法

Python对于类的成员没有严格的访问控制限制,这与其他面向对象语言有区别。关于私有属性和私有方法,有如下要点:
1、通常我们约定,两个下划线开头的属性时私有的,其他为公共的
2、类内部可以访问私有属性
3、类外部不能直接访问私有属性
4、类外部可以通过”__类名__私有属性(方法名)”访问私有属性(方法)

#测试私有属性

class Employee:
 def init(self,name,age):
  = name
 self.__age = agee=Employee(“高”,18)
 print()

print(e.age) 属性前双下划线为私有,报错不存在属性

print(dir(e))# 打印dir出现属性,[’_Employee__age’, ‘class’, ‘delattr’, ‘dict’,……
 print(e._Employee__age)# 访问私有属性 _类名__属性名

私有方法
#测试私有属性
class Employee:
__conpany = “百战程序员”
def init(self,name,age):
= name

# 私有属性
    self.__age = age

# 私有方法
def __work(self):
    print("好好工作挣钱")
    print("年龄:{0}".format(self.__age))
    print(Employee.__conpany)

e=Employee(“高”,18)
print()

print(e.age) 属性前双下划线为私有,报错不存在属性

print(dir(e))# 打印dir出现属性,[’_Employee__age’, ‘class’, ‘delattr’, ‘dict’,……
print(e._Employee__age)# 访问私有属性 _类名__属性名

print(dir(实例名)) [’_Employee__age’, ‘_Employee__work’, ‘class’,

调用私有方法

e._Employee__work()

print(dir(实例名)) [’_Employee__age’, ‘_Employee__conpany’, ‘_Employee__work’, ’

调用私有属性

print(Employee._Employee__conpany)

装饰器 property
方法调用变换为属性调用
#测试@proprety的用法

class Employee:

@property

def salary(self):

print(“xxx”)

return 100000

emp1 = Employee()

print(emp1.salary)

class Employee:
 def init(self,name,salary):
 self.__name = name
 self.__salary = salary
 def get_salary(self):
 return self.__salary
def set_salary(self,salary):
    if salary<10000 and salary>5000:
        self.__salary=salary
    else:
        print("录入错误")
emp1 = Employee(“xxx”,200)
 print(emp1.get_salary())
 emp1.set_salary(90000)
 print(emp1.get_salary())class Em2:
 def init(self,name,salary):
 self.__name = name
 self.__salary = salary
 @property
 def salary(self):
 return self.__salary
 @salary.setter
 def salary(self,salary):
 if 1000<salary<50000:
 self.__salary = salary
 else:
 print(“录入错误”)em1 = Em2(“bb”,10000)
 print(em1.salary)
 em1.salary=20000
 print(em1.salary)

面向对象三大特征
封装
隐藏对象的属性和实现细节,只对外提供必要的方法,相当于将“细节封装起来”,只对外暴露相关调用方法
通过前面学习的私有属性和私有方法的方式,实现封装。Python追求简洁的语法,没有严格的语法级别的访问控制符,更多的程序员去实现
继承
继承可以让子类具有父类的特性,提高了代码的重用性
从设计上是一种增量进化,原有父类设计不变的情况下,可以增加新的功能,或者改进已有的算法
多态
多态是指同一个方法调用由于对象不同会产生不同的行为。生活中这样的例子比比皆是,同样是休息方法。人不同休息方法不同。张三睡觉,李四玩游戏,程序员休息敲代码。

继承

动物–(爬行动物、哺乳动物)—(各种动物)
语法格式
Python支持多重继承,一个子类可以继承多个父类,继承的语法格式如下:
Class 子类类名(父类1[,父类2,…])
类体
如果在类定义中没有指定父类,则默认父类是object类。也就是说,object类是所有类的服了,里面定义了一些所有类共有的默认实现,比如__new__
定义子类时,必须在其构造函数中调用父类的构造函数。调用格式如下:
父类名.init(self,参数列表)

测试继承的基本使用

class Person:
 def init(self,name,age):
  = name
 self.__age = age #私有化age属性
 def say_age(self):
 print(“年龄,我也不知道”)class Student(Person):
 def init(self,name,age,score):
 Person.init(self,name,age) #必须是显式的调用父类初始化方法,不然解释器不会去调用
 self.score = score

Student–Person–object

print(Student.mro()) #打印类的层次
 s = Student(“xx”,18,60)
 s.say_age()#说明父类的方法被继承了
 print()

print(s.age) 父类age私有化了,会报错

print(s._Person__age)

类成员的继承和重写
1、成员继承:子类继承了父类出构造方法之外的所有成员
2、方法重写:子类可以重新定义父类中的方法,这样就会覆盖父类的方法,称为重写

class Person:
 def init(self,name,age):
  = name
 self.__age = age
def say_age(self):
    print("我的年龄是{0}".format(self.__age))

def say_introduce(self):
    print("我的名字是{0},年龄是{1}".format(,self.__age))

c

lass Student(Person):
 def init(self,name,age,score):
 Person.init(self,name,age)
 self.score = score
 def say_age(self):
 ‘’‘重写父类的方法’’’
 print(“学生的年龄是{0}”.format(self._Person__age))
 def say_introduce(self):
 print(“自我介绍,名字{0},年龄{1}”.format(,self._Person__age))s = Student(“xx”,18,60)
 print(dir(s))
 s.say_age()
 s.say_introduce()查看类的继承层次结构
 通过类的方法mro()或者类的属性_mro_可以输出这个类的继承层次结构
 class A:
 pass
 class B(A):
 pass
 class C(B):
 pass
 print(C.mro())执行结果如下
[, , , ]Object根类
 class Person:
 def init(self,name,age):
  = name
 self.age = age
 def say_age(self):
 print(,“的年龄是:”,self.age)obj = object()
 print(dir(obj))
 s2=Person(“xx”,19)
 print(dir(s2))
 ‘’’
 [‘class’, ‘delattr’, ‘dir’, ‘doc’, ‘eq’, ‘format’, ‘ge’, ‘getattribute’, ‘gt’, ‘hash’, ‘init’, ‘init_subclass’, ‘le’, ‘lt’, ‘ne’, ‘new’, ‘reduce’, ‘reduce_ex’, ‘repr’, ‘setattr’, ‘sizeof’, ‘str’, ‘subclasshook’]
 [‘class’, ‘delattr’, ‘dict’, ‘dir’, ‘doc’, ‘eq’, ‘format’, ‘ge’, ‘getattribute’, ‘gt’, ‘hash’, ‘init’, ‘init_subclass’, ‘le’, ‘lt’, ‘module’, ‘ne’, ‘new’, ‘reduce’, ‘reduce_ex’, ‘repr’, ‘setattr’, ‘sizeof’, ‘str’, ‘subclasshook’, ‘weakref’, ‘age’, ‘name’, ‘say_age’]
 ‘’’


Obj的所有属性,s2都有
从打印出的属性我们可以发现:
1、子类实例增加了 ‘dict’ ‘module’ ‘weakref’ ‘age’ ‘name’ ‘say_age’
2、子类属性继承了父类的所有属性
3、我们打印age、name、say_age,发现say_age虽然是方法,实际上也是属性,只不过,这个属性的类型是’method’
age <class ‘int’>
name <class ‘str’>
say_age <class ‘method’> ?这里打印方法的属性为何是Nonetype

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

class Person:
 def init(self,name):
  = name
def __str__(self):
    return 

p = Person(“xxx”)

print§

#<main.Person object at 0x000001F3AF542D88>
p = Person(“xxx”)
print§

xxx

多重继承
Python支持多重继承,一个子类可以有多个“直接父类”。这样就具备了“多个父类的特点”。但是因为这样,类的整体层次搞的异常复杂,尽量避免使用
MRO(method resolution order):方法解析顺序。我们可以通过mro()方法获得类的层次结构,方法解析顺序也是按照这个类的层次结构寻找的

class A:
 def aa(self):
 print(“aa”)
 class B:
 def bb(self):
 print(“bb”)
 class C(A,B):
 def cc(self):
 print(“cc”)
 c=C()
 c.cc()
 ()
 c.aa()
 ‘’’
 cc
 bb
 aa
 ‘’’class A:
 def aa(self):
 print(“aa”)
 def say(self):
 print(“AAA”)
 class B:
 def bb(self):
 print(“bb”)
 def say(self):
 print(“BBB”)
 class C(A,B):
 def cc(self):
 print(“cc”)
 c=C()
 print(C.mro())
 c.say() #打印 AAA 谁在前面就调用谁的方法Super()获取父类定义
 在子类中,如果想要获得父类的方法,我们可以使用super来定义
 Super代表父类的定义,不是父类的对象
 class A:
 def say(self):
 print(“A:”,self)
 class B:
 def say(self):
 # A.say(self)
 print(“B:”,self)
 B().say()
 ‘’’
 A: <main.B object at 0x000001D9F38BF108>
 B: <main.B object at 0x000001D9F38BF108>
 ‘’’class A:
 def say(self):
 print(“A:”,self)
 class B(A):
 def say(self):
 super().say()
 ‘’’
 A: <main.B object at 0x000001FBB501F1C8>
 B: <main.B object at 0x000001FBB501F1C8>
 ‘’’
 print(“B:”,self)
 B().say()
 ‘’’
 A: <main.B object at 0x000001D9F38BF108>
 B: <main.B object at 0x000001D9F38BF108>

多态
指的是同一个方法调用由于对象的不同可能产生不同的行为。比如人休息的不同方式
多态要注意两点:
1、多态是方法的多态,属性没有多态
2、多态的存在有2两个必要的条件:继承和方法重写
#多态

class Man:
 def eat(self):
 print(“人吃饭”)
 class Chinese(Man):
 def eat(self):
 print(“中国人用筷子吃饭”)
 class English:
 def eat(self):
 print(“英国人用叉子吃饭”)
 class Indian:
 def eat(self):
 print(“印度人用手吃饭”)def maneat(m):
 if isinstance(m,Man):
 m.eat()
 else:
 print(“不能吃饭”)maneat(Chinese()) #不能吃饭 未继承所有不能吃饭
 maneat(Indian())#不能吃饭 未继承所以不是人 不能吃饭

特殊方法和运算符重载
加法重载

a=20
 b=30
 c=a+b
 d=a.add(b)
 print(c,d)
 print("#############")
 class Person:
 def init(self,name):
  = name
 def add(self, other):
 if isinstance(other,Person):
 return “{0}–{1}”.format(,)
 else:
 return “不是同类对象不能相加”
 def mul(self, other):
 if isinstance(other,int):
 return *int
 else:
 return
 p1 = Person(“xx”)
 p2 = Person(“bb”)
 x=p1+p2 #xx–bb
 print(x)print("######")
 p1 = Person(“aa”)
 p2 = Person(12)
 x=p1+p2
 print(x)
 #重写了乘法后
 print(x*3)特殊属性
 python对象中包含了很多双下划线开始和结束的属性,这些是特殊属性,有特殊用法。这里我们列出常见的特殊属性:拷贝
 #测试对象的浅拷贝、深拷贝
 import copy
 class MobilePhone:
 def init(self,cpu,screen):
 self.cpu = cpu
 self.screen = screen
 class CPU:
 def calculate(self):
 print(“计算”)
 print(“cpu对象”,self)class Screen:
 def show(self):
 print(“显示”)
 print(“screen对象”,self)#测试变量赋值
m1 = MobilePhone
m2 = m1
print(m1)
print(m2)#为同一值
#测试浅复制
 c1=CPU()
 c2=c1print(c1)
print(c2)
s1=Screen()
 m1 = MobilePhone(c1,s1)
 m2 = copy.copy(m1)
 print(m1)
 print(m2)

发现不一样

print(m1,m1.cpu,m1.screen)
 print(m2,m2.cpu,m2.screen)

发现m1和m2不一样,cpu和屏幕一样

#测试深复制

print()
 m3 = copy.deepcopy(m1)
 print(m1,m1.cpu,m1.screen)
 print(m3,m3.cpu,m3.screen)

发现m1和m3不一样,cpu和屏幕都不一样

<_main

组合
“is-a”关系,我们可以使用“继承”。从而实现子类拥有的父类方法和属性。“is-a”gua关系指的是类似这样的关系:狗是动物,dog is animal狗类就应该继承动物类
“has-a”关系,我们可以使用组合,也能实现一个类拥有另一个类的方法和属性。Has-a
关系指的是这样的关系:手机拥有CPU。Mobilephone has a CPU

测试组合

class A1:
 def say_a1(self):
 print(“a1,a1,a1”)
 class B1(A1):
 pass
 b1 = B1()
 b1.say_a1()class A2:
 def say_a2(self):
 print(“a2,a2”)class B2:
 def init(self,a):
 self.a = a
 a2 = A2()
 b2 = B2(a2)
 b2.a.say_a2()

工厂模式的实现
设计模式是面向对象语言特有的内容,使我们在面临某一类问题时候固定的做法,设计模式有很多种,比较流行的是 GOF23种设计模式。当然,我们没有必要全部学习,学习几个常用的即可。
对于初学者,我们学习最常用的模式:工厂模式和单例模式。
工厂模式实现了创建者和调用者的分离,使用专门的工厂类将选择实现类、创建对象进行统一的管理和控制
#测试工厂模式

class CarFactory:
 def creat_car(self,brand):
 if brand==“奔驰”:
 return Benz()
 elif brand==“宝马”:
 return BMW
 elif brand==“比亚迪”:
 return BYD
 else:
 return “未知品牌”class Benz:
 pass
 class BMW:
 pass
 class BYD:
 passfactory = CarFactory()
 c1 = factory.creat_car(“奔驰”)
 print(type(c1))

单例模式
单例模式的核心作用是确保一个类只有一个实例,并且提供一个访问该实例的全局访问点。
单例模式只生成一个实例对象,减少了对系统资源的开销。当一个对象的产生需要比较多的资源,如读取配置文件、产生其他依赖对象时,可以产生一个“单例对象”,然后永久驻留内存中,从而极大的降低开销

实操作业

1、如下代码测试对象的浅拷贝、深拷贝,请绘制出内存示意图

python同名函数相互冲突 python 同名方法_python


python同名函数相互冲突 python 同名方法_子类_02


2. 定义发动机类 Motor、底盘类 Chassis、座椅类 Seat,车辆外壳类 Shell,并使用组合 关系定义汽车类。其他要求如下: 定义汽车的 run()方法,里面需要调用 Motor 类的 work()方法,也需要调用座椅 类 Seat 的 work()方法,也需要调用底盘类 Chassis 的 work()方法。

  1. 使用工厂模式、单例模式实现如下需求: (1) 电脑工厂类 ComputerFactory 用于生产电脑 Computer。工厂类使用单例模式, 也就是说只能有一个工厂对象。 (2) 工厂类中可以生产各种品牌的电脑:联想、华硕、神舟 (3) 各种品牌的电脑使用继承实现: (4) 父类是 Computer 类,定义了 calculate 方法 (5) 各品牌电脑类需要重写父类的 calculate 方法
  2. 定义一个 Employee 雇员类,要求如下: (1) 属性有:id、name、salary (2) 运算符重载+:实现两个对象相加时,默认返回他们的薪水和 (3) 构造方法要求:输入 name、salary,不输入 id。id 采用自增的方式,从 1000 开 始自增,第一个新增对象是 1001,第二个新增对象是 1002。 (4) 根据 salary 属性,使用@property 设置属性的 get 和 set 方法。set 方法要求输 入:1000-50000 范围的数字。