1、函数式编程和面向对象的对比
2、面向对象代码如何实现
3、面向对象三个特性:封装/继承/多态
(1) 面向过程
(2) 函数式编程 : 提高代码重用性
def func(arg): pass func(1) func(2)
(3) 为什么要将某些函数写在指定文件中
模块的划分,对函数进行归类
函数式编程和面向对象对比1(归类)
开发一个消息提醒的功能(邮件/短信/微信)
# 函数式编程 def email(em, text): """ 发送邮件 """ pass def msg(tel, text): """ 发送短信 """ pass def wechat(num, text): """ 发送微信 """ pass # 编写一个功能,假设用户完成购买,发送短信,邮件,微信 if 1 == 1: msg('15851816236', "XXX完成购买") email('canaan@edu.com', "XXX完成购买") wechat('123456', "XXX完成购买")
# 面向对象编程 # 写一个类名,里面再写方法 class Message: def __init__(self): pass def email(self, em, text): pass def msg(self, tel, text): pass def wechat(self, num, text): pass # 调用 if 1 == 1: obj = Message() # obj会成为方法的第一个参数默认传入 obj.msg('15851816236', "XXX完成购买") obj.email('canaan@edu.com', "XXX完成购买") obj.wechat('123456', "XXX完成购买")
函数的优点:定义简单,调用简单
面向对象:定义复杂,调用复杂;将某些类似的功能写在了一起(归类)
面向对象方式定义:
class 类名: # 定义一个类 def 函数名(self): # 在类中编写了一个方法 pass
调用:
x1 = 类名() # 创建了一个对象--实例化一个对象 x1.函数名() # 通过对象,调用其中方法
示例:
def login(): user = input("请输入用户名: ") pwd = input("请输入密码: ") if user == "Canaan" and pwd == "1234560": print("登录成功") else: print("登录失败") login()
写一个面向对象版本的:
class Account: def login(self): user = input("请输入用户名: ") pwd = input("请输入密码: ") if user == "Canaan" and pwd == "1234560": print("登录成功") else: print("登录失败") obj = Account() obj.login()
函数式编程和面向对象对比2(数据封装)
需求:打印
canaan/233岁/女/喜欢打游戏
canaan/233岁/女/喜欢学习
函数式编程
""" 完成以下功能: canaan/233岁/女/喜欢打游戏 canaan/233岁/女/喜欢学习 canaan/233岁/女/喜欢看电影 """ def games(name, age, gender): data = "%s, 今年%s岁, 性别%s,喜欢打游戏" %(name, age, gender) print(date) def study(name, age, gender): data = "%s, 今年%s岁, 性别%s,喜欢学习" %(name, age, gender) print(date) def move(name, age, gender): data = "%s, 今年%s岁, 性别%s,喜欢看电影" %(name, age, gender) print(date) games('canaan', '233', '女') study('canaan', '233', '女') move('canaan', '233', '女') # 重复传参 # 如果可以把这些参数,放在指定的一堆,不再需要重复传递
面向对象编程
class Canaan: def __init__(self, name, age, gender): # 实例化时,自动执行 pass def games(self, name, age, gender): data = "%s, 今年%s岁, 性别%s,喜欢打游戏" %(name, age, gender) print(date) def study(self, name, age, gender): data = "%s, 今年%s岁, 性别%s,喜欢学习" %(name, age, gender) print(date) def move(self, name, age, gender): data = "%s, 今年%s岁, 性别%s,喜欢看电影" %(name, age, gender) print(date) canaan = Canaan()
__init__
方法什么时候被调用?
当实例化一个对象时,这个方法就会被自动执行
实例化:类名后面带一个括号,并赋值给一个对象名,就是一个实例化的过程
需要保证自动执行时,带着参数是正确的,也就是说,需要给这个对象实例化时,应该有的属性
obj = ClassName()
# 这里就不用加参数了,可以直接从对象容器(self)中取 def games(self, name, age, gender): pass def games(self): data = "%s, 今年%s岁, 性别%s,喜欢打游戏" %(self.name, self.age, self.gender) print(data) # 调用类的方法时,就不用传参了,三个参数都已经被打包放到了实例化的对象(在类中表现为self)中,类中的实例化方法,只要去self容器中取参数就可以了 canaan.games() canaan.study() canaan.move()
__init__
方法的作用[构造方法]:
把和实例化对象有关的值打包,初始化时就封装到了实例化对象(self)中,使用时用.
获取即可
当方法被重复调用,需要多次重复传参
在__init__
方法中给一个默认值
class Foo: def __init__(self, name): self.name = name self.age = 18 obj = Foo("xxx") # obj里面有两个值 class Bar: pass obj = Bar() # obj里面是空的
应用1:
如果许多方法,需要共同的参数时,可以改成面向对象的方式,把数据打包obj(self)中,其他的方法只要送self中进行获取
将数据封装到对象中,以便在方法中调用
class FileHandler: # 把一些共同的参数打包放到self里(实例化对象的属性里) def __init__(self, file_path): self.file_path = file_path self.f = open(self.file_path, 'rb') # 把打开文件对象也封装在__init__()中,实例化方法进行调用就可以了 # 这些方法又有共同的参数 def read_first(self): # 如果这些方法,都需要打开文件这个操作 # f = open(self.file_path, 'rb') self.f.read pass def read_last(self): # f = open(self.file_path, 'rb') self.f.read pass def read_second(self): # f = open(self.file_path, 'rb') self.f.read pass obj = FileHandler('C:xxxx..') obj.read_first() obj.read_second() obj.read_last() obj.f.close()
应用2:
需要大量传参时
def func(a1, a2, a3, a4,...): pass def func(*args): arg[1] # 通过索引来获取传参 arg[5] def func(**kwargs): kwargs['a1'] kwargs['a5'] func(a1='123', a2='456', a3='9') # 这里就有一个打包的思想 如果使用面向对象 def new_func(arg): arg.k1 arg.k2 arg.k3 # 作用: 不会动态的传入,只会传入确定的 # 数据进行封装,给别人使用时 class Foo: def __init__(self, k1, k2, k3): self.k1 = k1 self.k2 = k2 self.k3 = k3 obj = Foo(111, 22, 3) new_func(obj) # 参数打包到一个对象中
应用3:
需求:个人信息管理系统
1、用户登录
2、显示当前用户信息
3、查看当前用户所有的账单
4、购买一件商品
class UserInfo: def __init__(self): self.name = None # 最开始无值,是空的 def info(self): # 现在实现方法就不用传参了,在类的内部可以进行封装 print("当前用户为: %s" %(self.name, )) def account(self): print("当前用户%s账单为xxx" %(self.name, )) def shopping(self): print("%s购买了一件商品" %(self.name, )) def login(self): user = input("请输入用户名:") pwd = input("请输入密码:") if pwd == "123456": self.name = user # 不仅能从封装的self中获取值,还是可以继续在self里面封装 while True: print(""" 1、查看用户信息 2、查看用户账单 3、购买商品 """) num = int(input("请输入选择")) if num == 1: self.info() # 内部调用当前类中的"实例化方法", 等价于obj.info(), obj传递到类中就是self elif num == 2: self.account() elif num == 3: self.shopping() else: print("序号不存在,请重新输入") else: print("登录失败") canaan = UserInfo() canaan.login()
小结:
class Foo: def func2(self): print("func2") def func1(self): self.func2() print("func1") obj = Foo() obj.func1()
self永远指的是实例化对象
面向对象的代码应该如何编写
a、规则
class Foo: def __init__(self, name): self.name = name def detail(self, msg): print(selg.name, msg) obj = Foo('xxx') obj.detail('Hello')
b、如何写?
方式1:归类+提取公共值
方式2:正向编写,在指定类中编写和当前类相关的所有代码,提取公共值
# 和发消息有关的 class Message: def email(self): pass # 和人有关的 class Person: def run(self): pass
面向对象的三大特性:封装、继承和多态
封装:
-
将相关功能(方法),封装到一个类中
-
将数据(属性)封装到一个对象中
继承:
# ############ 继承的基本使用 ############ class SuperBase: def f3(self): print("f3") class Base(SuperBase): # 父类/基类 def f2(self): print("f2") # 继承 class Foo(Base): # 子类/派生类 def f1(self): print("f1") obj = Foo() obj.f1() obj.f2() # 子类(自己)没有,就去父类找 obj.f3() # 父类还是没有,就去父类的父类找 # 查找原则: 子类没有,就去父类找
为什么要有继承关系:为了复用,提高代码的重用性
# ############ 继承是为了提高代码的重用性 ############ class Base: def f1(self): pass class Foo(Base): # def f1(self): # pass def f2(self): pass class Bar(Base): # def f1(self): # 如果这里的f1和Foo类中的f1一模一样,那么这段代码就是重复的,没必要写两遍 # pass def f2(self): pass
继承中的多继承
# ############ 多继承 ############ # python可以继承多个 class Base1: def show(self): print("Base1.show") class Base2: def show(self): print("Base2.show") class Foo(Base1, Base2): # 支持多继承,查找顺序从左到右 pass obj = Foo() obj.show()
继承练习
############ 练习1 ############ class Base: def f1(self): print("f1") class Foo(Base): def f2(self): print("f2") # # 1 # obj = Foo() # obj.f2() # 从自己开始找 # obj.f1() # 自己没有找父类的 # 2 obj = Base() obj.f1() obj.f2() # 出错,子类可以调用父类的方法,父类不能使用子类的方法
############ 练习2 ############ class Base: def f1(self): print("Base.f1") class Foo: def f3(self): print("Foo.f3") def f2(self): print("Foo.f2") self.f3 # obj是哪个类(Foo)的对象,那么执行方法时,就从该类开始找 obj = Foo() obj.f2() # obj是哪个类(Foo)的对象,那么执行方法时,就从该类开始找
############ 练习3 ############ class Base: def f1(self): print("Base.f1") def f3(self): print("Foo.f3") class Foo(Base): def f2(self): print("Foo.f2") self.f3() # 一定要看self是谁的对象,和"自己"无关,self是谁的对象就从哪里开始找 obj = Foo() obj.f2()
############ 练习4 ############ class Base: def f1(self): print("Base.f1") def f3(self): self.f1() # 这里的self还是obj,调用self方法时,在python中传递的就是实例化对象,因此这里的self还是Foo的对象 print("Foo.f3") class Foo(Base): def f2(self): print("Foo.f2") self.f3() obj = Foo() obj.f2()
############ 练习5 ############ class Base: def f1(self): print("Base.f1") def f3(self): self.f1() # self中传入的是obj,而obj一直都是Foo类的对象,所以这里调用的是子类的f1()方法 print("Base.f3") class Foo(Base): def f1(self): print("Foo.f1") def f2(self): print("Foo.f2") self.f3() # 由于Foo类中不存在f3()方法,这里使用的是Base类的f3()方法 obj = Foo() obj.f2() # 类中的self并不是在哪个类中,调用的是哪个类的方法,而是属于哪个类的对象,就去哪个类中找 obj2 = Base() # 属于Base类中的对象 obj2.f3()
弄清楚,self到底是哪个类的中的对象,就从该类开始找,自己没有,找父类
############ 练习6 ############ class Base1: def f1(self): print("Base1.f1") def f2(self): print("Base1.f2") class Base2: def f1(self): print("Base2.f1") def f2(self): print("Base2.f2") def f3(self): print("Base2.f3") self.f1() # self还是Foo的对象obj,因此还是先回去Foo类中,没找到再去按多继承的顺序从左到右找 # 多继承时,先找左边的 # self是哪个类的对象,就从哪个类开始找 class Foo(Base1, Base2): def f0(self): print("Foo.f0") self.f3() obj = Foo() obj.f0()
小结:
-
类的继承编写:
class Foo(父类): pass
-
支持多继承:
查找顺序:永远先从实例化的类开始找,然后再依次从左往右一次查找父类中的方法
class Foo(Base1, Base2...)
-
为什么要有多继承?
提高代码重用性
多态:
多种形态或多种状态——鸭子模型(只要可以"嘎嘎"叫,那就是鸭子)
def func(arg): # python原生就支持多态 arg.send() arg[0] func([11, 33]) func((1, 2, 3)) func("abcd")
Python原生支持多态,所以没有特殊性,但是在Java或者别的语言中有限制
如果规定了类型,传参是可以是类对象,也可以是类的任何子类的对象
class Foo1: def f1(self): pass class Foo2: def f1(self): pass class Foo3: def f1(self): pass def func(arg): arg.f1() obj = Foo1() # obj = Foo2 obj = Foo3 func(obj)
1、面向对象编写
2、如果归类:类相关的功能(方法)+提供公共值(属性)
面向对象的成员和组合
1、面向对象解决什么问题:
-
归类(一个类的方法封装一个类中方法)
-
重复传参(封装到对象中,称为对象的属性)
-
提高代码重用性(子类继承父类)
2、继承:当存在两个类或者多个类中,有一些共同的属性或者方法,为了避免代码重复编写,可以放到一个基类中
多态:本身Python原生支持多态,崇尚“鸭子模型”,体现到代码上,就是写一个函数,传参时无需指定类型,arg可以是多种类型,只要这个对象,带有自身的方法,就能进行调用
3、编写面向对象程序
归类+提供公共值
4、self指的到底是什么(一个实例的对象本身)
self在类外是一个对象,在类内也表现为一个实例对象本身,self是python帮助自动传递的,传递到类中,然后在类方法中进行传递,如果执行面向对象方法,必须由一个实例本身进行执行
xxx.func1()
5、python支持多继承,注意查找顺序