2021-03-05

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):
self.name = name
self.__age = age

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

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

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

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

    # 私有属性
    self.__age = age

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

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

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):
self.name = 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(s.name)

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

print(s._Person__age)

类成员的继承和重写
1、成员继承:子类继承了父类出构造方法之外的所有成员
2、方法重写:子类可以重新定义父类中的方法,这样就会覆盖父类的方法,称为重写
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},年龄是{1}".format(self.name,self.__age))

class 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.name,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())

执行结果如下

[<class ‘main.C’>, <class ‘main.B’>, <class ‘main.A’>, <class ‘object’>]

Object根类
class Person:
def init(self,name,age):
self.name = name
self.age = age
def say_age(self):
print(self.name,“的年龄是:”,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):
self.name = name

def __str__(self):
    return self.name

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.bb()
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):
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*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=c1

print(c1)

print(c2)

s1=Screen()
m1 = MobilePhone(c1,s1)
m2 = copy.copy(m1)
print(m1)
print(m2)

<main.MobilePhone object at 0x00000222D506C8C8>

<main.MobilePhone object at 0x00000222D506C7C8>

发现不一样

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

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

<main.MobilePhone object at 0x00000205C9FCCA08> <main.CPU object at 0x00000205C9FCC808> <main.Screen object at 0x00000205C9FCC848>

<main.MobilePhone object at 0x00000205C9FCC908> <main.CPU object at 0x00000205C9FCC808> <main.Screen object at 0x00000205C9FCC848>

#测试深复制
print()
m3 = copy.deepcopy(m1)
print(m1,m1.cpu,m1.screen)
print(m3,m3.cpu,m3.screen)

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

<main.MobilePhone object at 0x00000205C9FCCA08> <main.CPU object at 0x00000205C9FCC808> <main.Screen object at 0x00000205C9FCC848>

<_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:
pass

factory = CarFactory()
c1 = factory.creat_car(“奔驰”)
print(type(c1))

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

实操作业
1、如下代码测试对象的浅拷贝、深拷贝,请绘制出内存示意图
在这里插入图片描述
在这里插入图片描述
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 范围的数字。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值