面向对象
5.1 面向对象的基本知识
5.1.1 基本概念
- 类:具有相同方法和属性的一类事物
- 对象(实例):一个拥有具体属性值和动作的具体个体
- 实例化:从一个类得到一个具体对象的过程
5.1.2 基本格式
# 定义类
class 类名:
def 方法名(self,name):
print(name)
return 123
def 方法名(self,name):
print(name)
return 123
def 方法名(self,name):
print(name)
return 123
# 调用类中的方法
# 1.创建该类的对象
obj = 类名()
# 2.通过对象调用方法
result = obj.方法名('alex')
print(result)
5.1.3 应用场景
-
应用场景:遇到很多函数,需要给函数进行归类和划分
-
补充:
-
类和对象是什么关系?
- 对象是类的一个实例
-
self是什么?
- self就是一个形式参数,对象调用方法时,python内部会将该对象传给这个参数
-
类内部代码会自上而下依次执行,类变量立即执行,方法需要调用内部代码才会执行
-
类与类之间可以嵌套,执行遵循上条
-
什么时候使用面向对象?
- 函数(业务功能)比较多,可以使用面向对象来进行分类
- 想要做数据封装(创建字典存储数据时,面向对象)
- 游戏示例:创建一些角色并且根据角色需要再创建人物
-
5.2面向对象的三大特性
5.2.1 封装
- 广义上的封装:类中的成员
- 狭义上的封装:私有成员
-
对象的作用:存储一些值,方便以后自己使用
class File(obje): def read(self): with open(self.xxxxx, mode='r', encoding='utf-8') as f: data = f.read() return data def write(self, content): with open(self.xxxxx, mode='a', encoding='utf-8') as f: f.write(content) # # 实例化了一个File类的对象 obj1 = File() # # 在对象中写了一个xxxxx = 'test.log' obj1.xxxxx = "test.log" # # 通过对象调用类中的read方法,read方法中的self就是obj。 # # obj1.read() obj1.write('alex') # 实例化了一个File类的对象 obj2 = File() # 在对象中写了一个xxxxx = 'test.log' obj2.xxxxx = "info.txt" # 通过对象调用类中的read方法,read方法中的self就是obj。 # obj2.read() obj2.write('alex')
-
封装的作用:将数据封装到对象,方便使用
class Person: def __init__(self,n,a,g): # 初始化方法(构造方法),给对象的内部做初始化。 self.name = n self.age = a self.gender = g def show(self): temp = "我是%s,年龄:%s,性别:%s " % (self.name, self.age, self.gender,) print(temp) # 类() 实例化对象,自动执行此类中的 __init__方法。 p1 = Person('李兆琪',19,'男') p1.show() p2 = Person('利奇航',19,'男') p2.show()
-
总结
-
如果写代码是,函数比较多比较乱
- 可以将函数归类并放到ton规格类中
- 函数如果有一个反复使用的公共值,则可以放到对象中
-
面向过程编程
# 示例:循环让用户输入:用户名/密码/邮箱。 输入完成后再进行数据打印。 # 以前的写法: USER_LIST = [] while True: user = input('请输入用户名:') pwd = input('请输入密码:') email = input('请输入邮箱:') temp = {'username':user,'password':pwd,'email':email} USER_LIST.append(temp) for item in USER_LIST: temp = "我的名字:%s,密码:%s,邮箱%s" %(item['username'],item['password'],item['email'],) print(temp)
-
面向对象编程
# 面向对象写法一: class Person: def __init__(self,user,pwd,email): self.username = user self.password = pwd self.email = email USER_LIST = [对象(用户/密码/邮箱),对象(用户/密码/邮箱),对象(用户/密码/邮箱)] while True: user = input('请输入用户名:') pwd = input('请输入密码:') email = input('请输入邮箱:') p = Person(user,pwd,email) USER_LIST.append(p) for item in USER_LIST: temp = "我的名字:%s,密码:%s,邮箱%s" %(item.username,item.password,item.email,) print(temp)
-
面向对象写法二:
class Person: def init(self,user,pwd,email): self.username = user self.password = pwd self.email = email
def info(self): return "我的名字:%s,密码:%s,邮箱%s" %(item.username,item.password,item.email,)
USER_LIST = [对象(用户/密码/邮箱),对象(用户/密码/邮箱),对象(用户/密码/邮箱)] while True: user = input('请输入用户名:') pwd = input('请输入密码:') email = input('请输入邮箱:') p = Person(user,pwd,email) USER_LIST.append(p) for item in USER_LIST: msg = item.info() print(msg)
-
5.2.2 继承
- 所有的查找名字(调用方法和属性),都是先找自己,自己没有的找父类
- 如果自己和父类都有,希望自己和父类都调用,就用super().指定类名字直接调用
-
基本内容
# 父类(基类) class Base: def f1(self): pass # 子类(派生类) class Foo(Base): def f2(self): pass # 创建了一个子类的对象 obj = Foo() # 执行对象.方法时,优先在自己的类中找,如果没有就是父类中找 obj.f2() obj.f1() # 创建了一个父类的对象 obj = Base() obj.f1()
-
总结
-
什么时候才能用到继承?
- 多个类中如果有公共的方法,可以放到基类中避免重复编写
-
继承关系中的查找方式的顺序时,注意事项
- self到底是谁
- self是哪个类创建的,就从此类开始找,自己没有就找父类
-
多继承,如果一个派生类有多个基类,执行对象方法时,优先在在自己的类中找,如果没有就从左到右依次从基类中找
-
-
补充(python2与python3的区别十一):
class Foo: pass class Foo(object): pass # 在python3中这俩的写法是一样,因为所有的类默认都会继承object类,全部都是新式类。 # 如果在python2中这样定义,则称其为:经典类 class Foo: pass # 如果在python2中这样定义,则称其为:新式类 class Foo(object): pass # 新式类 # 继承object # 支持super # 多继承 广度优先C3算法 # mro方法 # 经典类 # py2中不继承object # 没有super语法 # 多继承 深度优先 # 没有mro方法
-
补充:
-
新式类
-
继承object
-
支持super
-
多继承 广度优先C3算法
-
mro方法
class A: pass class B(A): pass class C(A): pass class D(B,C): pass d = D() print(d.mro())
-
-
经典类
- py2中不继承object
- 没有super语法
- 多继承 深度优先
- 没有mro方法
-
5.2.3 多态
- 一个类表现出来的多种状态------->多个类表现出相似的状态
-
基本内容:多种形态 / 多种类型,python本身就是多态的
def func(arg): # 多种类型,很多事物 arg.send() # 必须具有send方法,呱呱叫
-
面试题:什么是鸭子模型?
- 对于一个函数而言,Python对于参数的类型不会限制,那么传入参数时就可以是各种类型,在函数中如果有例如:arg.send方法,那么就是对于传入类型的一个限制(类型必须有send方法),这就是鸭子模型。类似于上述的函数我们认为只要能呱呱叫的就是鸭子(只有有send方法,就是我们想要的类型)
5.3 成员
5.3.1 类成员
注意:在一般的类中,没有类名一律不能调用类中方法
-
类变量(静态字段)
-
定义:写在类的下一级,和方法同一级
-
访问
- 类.类变量名()
- 对象.类变量名()
- 注意:如果对象中也有同名变量,则对象.类变量名()使用对象自己中的变量
-
面试题
# 面试题 class Base: x = 1 obj = Base() print(obj.x) # 先去对象中找,没有再去类中找 obj.y = 123 # 在对象中添加了一个y=123的变量 obj.x = 123 # 在对象中添加了一个X=123的变量,无法修改类变量 Base.x = 666 # 修改类变量,x = 666
-
总结:
- 找变量优先找自己的,自己没有再去父类或基类中找
- 修改或赋值只能在自己内部设置
-
-
绑定方法(普通的方法)
- 定义:至少有一个self对象
- 执行:先实例化对象,对象.绑定方法()
class Foo: def __init__(self): self.name = 123 def func(self, a, b): print(self.name, a, b) obj = Foo() obj.func(1, 2)
-
静态方法
-
定义
- @staticmethod装饰器
- 参数无限制(可以不传参数)
-
执行:
- 类.静态方法名()【推荐】
- 对象.静态方法名()【不推荐,容易混淆】
class Foo: def __init__(self): self.name = 123 @staticmethod def f1(): print(123) obj = Foo() Foo.f1() # 推荐 obj.f1() # 不推荐
-
-
类方法
-
定义:
- @classmethod装饰器
- 至少有cls参数(cls是当前类)
-
执行:
- 类.类方法名()【推荐】
- 对象.类方法名()【不推荐,容易混淆】
class Foo: def __init__(self): self.name = 123 @classmethod def f2(cls,a,b): print('cls是当前类',cls) print(a,b) obj = Foo() Foo.f2(1,2) # 推荐 obj.f2(1,2) # 不推荐
-
面试题(笔试)
- 问:@classmethod和@staticmethod的区别?
- 答:一个是类方法,一个是静态方法。定义: 类方法:用@classmethod做装饰器且至少有一个cls参数 静态方法:用@staticmethod做装饰器且参数无限制 调用: 类.方法直接调用,对象.方法也可以调用
-
-
属性
-
定义:
- @property
- 只能有一个参数
-
执行:
- 对象.方法名
- 后面不用加括号,只是减少点代码量
class Foo: @property def func(self): print(123) return 666 obj = Foo() result = obj.func print(result)
-
应用:
# 属性的应用 class Page: def __init__(self, total_count, current_page, per_page_count=10): self.total_count = total_count self.per_page_count = per_page_count self.current_page = current_page @property def start_index(self): return (self.current_page - 1) * self.per_page_count @property def end_index(self): return self.current_page * self.per_page_count USER_LIST = [] for i in range(321): USER_LIST.append('alex-%s' % (i,)) # 请实现分页展示: current_page = int(input('请输入要查看的页码:')) p = Page(321, current_page) data_list = USER_LIST[p.start_index:p.end_index] for item in data_list: print(item)
-
5.3.2 示例(对象)成员
-
实例成员:实例变量
-
实例(对象)之间的嵌套
- 函数:参数可以任意值
- 字典:对象和类都可以做字典的key和value
- 一个类的对象作为另一个对象的是实例变量
class School(object): def __init__(self,title,addr): self.title = title self.address = addr class ClassRoom(object): def __init__(self,name,school_object): self.name = name self.school = school_object s1 = School('北京','沙河') s2 = School('上海','浦东') s3 = School('深圳','南山') c1 = ClassRoom('全栈21期',s1) c1.name c1.school.title c1.school.address
class StackConfig(object): list_display = '李邵奇' def changelist_view(self): print(self.list_display) class UserConfig(StackConfig): list_display = '利奇航' class AdminSite(object): def __init__(self): self._register = {} def registry(self,key,arg=StackConfig): self._register[key] = arg def run(self): for key,value in self._register.items(): obj = value() obj.changelist_view() site = AdminSite() site.registry(1) site.registry(2,StackConfig) site.registry(3,UserConfig) site.run()
5.3.3 特殊成员(就是为了能够快速实现某种功能而生的)
-
__init__()
- 初始化方法
- 用途:用于给对象中赋值
class Foo: def __init__(self,a1): self.a1 = a1 obj = Foo('alex')
-
__new__()
,先于__init__
执行- 构造方法
- 用途:用于创建空对象
class Foo(object): def __init__(self): self.x = 123 def __new__(cls, *args, **kwargs): return object.__new__(cls) obj = Foo() # 先执行__new__方法,再执行__init__方法
-
__call__()
- 对象后面加括号执行该方法
class Foo(object): def __call__(self, *args, **kwargs): print('执行call方法') obj = Foo() obj() # 对象后面加括号,执行__call__方法 # 相当于Foo()()
-
网站示例
from wsgiref.simple_server import make_server def func(environ,start_response): start_response("200 OK", [('Content-Type', 'text/plain; charset=utf-8')]) return ['你好'.encode("utf-8") ] class Foo(object): def __call__(self, environ,start_response): start_response("200 OK", [('Content-Type', 'text/html; charset=utf-8')]) return ['你<h1 style="color:red;">不好</h1>'.encode("utf-8")] # 作用:写一个网站,用户只要来方法,就自动找到第三个参数并执行。 server = make_server('127.0.0.1', 8000, Foo()) server.serve_forever()
-
__getitem__
()、__setitem__()
、__delitem__
class Foo(object): def __setitem__(self, key, value): pass def __getitem__(self, item): return 'uuu' def __delitem__(self, key): pass obj1 = Foo() obj1['k1'] = 123 # 内部会自动调用 __setitem__方法 val = obj1['xxx'] # 内部会自动调用 __getitem__方法,用val接受返回值 del obj1['ttt'] # 内部会自动调用 __delitem__ 方法
-
__str__()
- 只有在打印对象时,会自动调用此方法,并将其返回值在页面上显示出来
class Foo(object): def __str__(self): return 'asdfasudfasdfsad' obj = Foo() print(obj) # obj是一个对象,打印__str__方法的返回值
class User(object): def __init__(self,name,email): self.name = name self.email = email def __str__(self): return "%s %s" %(self.name,self.email,) user_list = [User('二狗','2g@qq.com'),User('二蛋','2d@qq.com'),User('狗蛋','xx@qq.com')] for item in user_list: print(item)
-
__repr__()
-
如果有str和repr,打印对象 调用str方法,打印列表或其他对象容器,调用repr方法
-
如果只有str,无论什么时候都是调用str,repr也是同理
-
实际应用上,str面向对象,repr更多的是面向程序员
-
打印使用str,调用使用repr
class D(object): def __str__(self): return "a __str__" def __repr__(self): return "a __repr__" >>> dr = D() >>> print dr a __str__ >>> dr a __repr__
-
-
__dict__()
- 去对象中找到所有变量并将其转换为字典
class Foo(object): def __init__(self,name,age,email): self.name = name self.age = age obj = Foo('alex',19) val = obj.__dict__ # 去对象中找到所有变量并将其转换为字典 print(val)
-
__enter__()
、__exit__()
- 上下文管理
# 面试题 class Foo(object): def do_something(self): print('内部执行') # 第三步 class Context: def __enter__(self): print('进入') # 第一步 return Foo() def __exit__(self, exc_type, exc_val, exc_tb): print('推出') # 第四步 with Context() as ctx: print('内部执行') # 第二步 ctx.do_something()
-
__add__
- 两个对象相加是用到此方法
class Foo(object): def __add__(self, other): # self:obj1,other:obj2 return 123 obj1 = Foo() obj2 = Foo() val = obj1 + obj2 # 内部会自动调用 __add__方法,并用val接受返回值 print(val) # 123
-
__iter__
- 将一个对象变成可迭代对象时用到此方法
class Foo:
def __iter__(self):
return iter([1,2,3,4])
obj = Foo()
5.3.4 成员修饰符
-
公有,所有的地方都能够访问到
-
私有,只有自己才可以访问到,修饰符:__
# 示例一: class Foo: def __init__(self, name): self.__name = name def func(self): print(self.__name) obj = Foo('alex') # print(obj.__name),报错,无法访问 obj.func()
# 示例二: class Foo: __x = 1 @staticmethod def func(): print(Foo.__x) # print(Foo.__x),报错,无法访问 Foo.func()
# 示例三: class Foo: def __fun(self): print('msg') def show(self): self.__fun() obj = Foo() # obj.__fun(),报错,无法访问 obj.show()
-
强制访问私有,中间街上 _类名
class Foo: def __init__(self,name): self.__x = name obj = Foo('alex') print(obj._Foo__x) # 强制访问私有实例变量
5.4约束
-
又名:Foo抽象类 / 接口类
-
定义:就是给子类一个规范,让子类必须按照抽象类的规范来实现方法
-
示例
# 约束子类中必须写send方法,如果不写,则调用时候就报抛出 NotImplementedError class BaseMessage(object): def send(self,a1): raise NotImplementedError('子类中必须有send方法') class Msg(BaseMessage): def send(self): pass class Email(BaseMessage): def send(self): pass class DingDing(BaseMessage): def send(self): print('钉钉') obj = Email() obj.send()
5.5 反射
-
根据字符串的总是去某个对象中操作其他成员
-
方法:
-
通过对象来获取实例变量、绑定方法
-
通过类来获取模块的任意类变量、类方法、静态方法
-
通过模块名来获取模块中的任意变量(普通方法、函数、类)
-
通过本文件来获取本文件中的任意变量
- getattr(sys.modules[
__name__
],’变量名‘)
- getattr(sys.modules[
-
5.5.1 getattr(对象,字符串)
-
根据字符串的形式去某个对象中获取该成员
class Foo(object): def __init__(self,name): self.name = name obj = Foo('alex') # 获取变量 v1 = getattr(obj,'name') # 获取方法 method_name = getattr(obj,'login') method_name()
-
补充:
class Foo(object): def get(self): pass obj = Foo() # if hasattr(obj,'post'): # getattr(obj,'post') v1 = getattr(obj,'get',None) # 推荐 print(v1)
5.5.2 hasattr(对象,字符串)
-
根据字符转的形式去某个是对象中判断是否有该成员
from wsgiref.simple_server import make_server class View(object): def login(self): return '登陆' def logout(self): return '等处' def index(self): return '首页' def func(environ,start_response): start_response("200 OK", [('Content-Type', 'text/plain; charset=utf-8')]) obj = View() # 获取用户输入的URL method_name = environ.get('PATH_INFO').strip('/') if not hasattr(obj,method_name): return ["sdf".encode("utf-8"),] response = getattr(obj,method_name)() return [response.encode("utf-8") ] # 作用:写一个网站,用户只要来方法,就自动找到第三个参数并执行。 server = make_server('192.168.12.87', 8000, func) server.serve_forever()
5.5.3 setattr(对象,字符串,值)
-
根据字符串的形式去某个对象中设置 / 修改该成员
class Foo: pass obj = Foo() obj.k1 = 999 setattr(obj,'k1',123) # obj.k1 = 123 print(obj.k1)
5.5.4 delattr(对象,字符串)
-
根据字符串的形式去某个对象中删除该成员
class Foo: pass obj = Foo() obj.k1 = 999 delattr(obj,'k1') print(obj.k1)
5.5.5 python一切皆对象
-
对象的定义
-
狭义:类实例化出来的就是对象
-
广义:
- 模块(py文件)及其内部成员
- 包(文件夹)及其内部成员
- 类及其内部成员
- 狭义的对象
-
-
python一切皆对象,所以以后象牙通过字符串的形式操作其内部成员都可以通过反射机制实现
5.6 单例模式(设计模式)
5.6.1 定义:无论实例化多少次,永远用的都是第一次实例化出的对象
5.6.2 应用场景:数据库连接,数据库连接池
5.6.3 单例模式标准
class Singleton(object):
instance = None
def __new__(cls, *args, **kwargs):
if not cls.instance:
cls.instance = object.__new__(cls)
return cls.instance
obj1 = Singleton()
obj2 = Singleton()
# 不是最终,加锁。
- 文件连接池
class FileHelper(object):
instance = None
def __init__(self, path):
self.file_object = open(path,mode='r',encoding='utf-8')
def __new__(cls, *args, **kwargs):
if not cls.instance:
cls.instance = object.__new__(cls)
return cls.instance
obj1 = FileHelper('x')
obj2 = FileHelper('x')
5.6.4 通过模块导入的特性也可以实现单例模式:
# jd.py
class Foo(object):
pass
obj = Foo()
# app.py
import jd # 加载jd.py,加载最后会实例化一个Foo对象并赋值给obj
print(jd.obj)
5.7 项目目录
5.7.1 包及其内部文件示例
-
可执行文件:bin
- 将所有的可执行文件放入其中
-
业务相关:src
- 登录注册相关:account.py
- 订单相关:order.py
- 主程序:run.py
-
公共类库:lib
- 分页功能
-
数据库:db
- 用户资料:user.txt
-
配置:config
- 设置:settings
-
日志存储:log
- 所有的日志文件
5.7.2 注意:每个可执行文件必须把项目目录加入sys.path中
import os
import sys
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)