初识面向对象
1. 面向过程: 一切以事物的流程为核心. 核心是"过程"二字, 过程是指解决问题的步骤, 即, 先干什么, 后⼲什么. 基于该思想编写程序就好比在编写一套流水线. 是一种机械式的编程思维
优点: 负责的问题流程化, 编写相对简单
缺点: 可扩展性差
2. 面向对象: 一切以对象为中⼼.
什么是对象? 不好解释. 先解释解释么是车? 有轱辘, 有方向盘, 有发动机, 会跑的 是车. 好. 在解释一个. 什么是人. 有名字, 年龄, 爱好, 会唱歌跳舞思考的是人. 我们给这两个 东西下了一个简单的定义. 总结: 具有相同属性和动作的结合体叫对象. 面向对象思维, 要⾃己建立对象. ⾃己建立场景. 你是就是面向对象世界中的上帝. 你想让车干嘛就干嘛. 你想让人干嘛人就能干嘛.
优点: 可扩展性强
缺点: 编程的复杂度高于面向过程
创建类:
class 类名:(类名首字母一般大写)
创建对象:
变量(引用, 句柄) = 类名(参数)
# 访问对象的属性信息
对象.属性
# 方法的访问:
对象.方法()
总结:
类与对象的关系: 类是对事物的总结. 抽象的概念. 类用来描述对象. 对象是类的实例化的结果. 对象能执行哪些方法. 都由类来决定. 类中定义了什么. 对象就拥有什么
3.⾯向对象三大特征:
封装, 继承, 多态. 只要是面向对象编程语言. 都有这三个特征.
1. 封装: 把很多数据封装到一个对象中. 把固定功能的代码封装到一个代码块, 函数, 对 象, 打包成模块. 这都属于封装的思想.
2. 继承: 子类可以自动拥有父类中除了私有属性外的其他所有内容. 说白了, 儿子可以 随便便用爹的东西. 但是朋友们, 一定要认清楚一个事情. 必须先有爹, 后有儿子. 顺序不能乱, python中实现继承非常简单. 在声明类的时候, 在类名后面添加一个小括号, 就可以完成继承关系.两个类具有相同的功能或者特征的时候. 可以采用继承的形式. 提取⼀一个父类, 这个父类 中编写着两个类相同的部分. 然后两个类分别取继承这个类就可以了了. 这样写的好处 是我们可以避免写很多重复的功能和代码.
3. 多态: 同一个对象, 多种形态. 这个在python中其实是很不容易说明⽩的. 因为我们⼀ 直在用. 只是没有具体的说. 比如. 我们创建一个变量a = 10 , 我们知道此时a是整数 类型. 但是我们可以通过程序让a = "ex", 这时, a又变成了字符串类型. 这是我们都知道的. 但是, 我要告诉你的是. 这个就是多态性. 同一个变量a可以是多种形态. 可能这样的程序和说法你还get不到具体什么是多态.
多态的好处: 程序具有超高的可扩展性. 面向对象思想的核心与灵魂. python自带多态
一.类的成员
class 类名:
# 方法
def __init__(self, 参数1, 参数2....):
# 属性变量
self.属性1 = 参数1
self.属性2 = 参数2 ....
# 方法
def method(self):
pass
在上面代码中__init__和method都属于类的成员方法. 又称为实例⽅法. 总之这样的东⻄ 需要用对象来访问. 而上方的self.属性1 = 参数1 这个代码的含义是给对象设置属性信息. 含义是这个对象的xxx属性是xxxx. 这种东西又被称之为成员变量或者实例变量, 再或者被称之为字段. 都是一个意思.
二. 类的成员-变量
在类中变量分成两⼤类: 1. 实例变量(字段) 2. 类变量(静态变量)
来做个简单的总结:
实例变量, 给对象用的.
类变量, 多个对象共享的. 最好是用类名来访问. 这样更加规范.
二. 类的成员-方法
1. 成员方法(实例⽅法)——对象直接访问的⽅法叫成员方法.
2. 静态方法
静态方法不需要我们给方法传递self. 也就是说. 当出现一个方法不需要使用到成员变量的时候. 就可以选择使用静态方法. 静态方法需要我们在方法上面添加一 个@staticmethod
3. 类方法
类方法和静态方法差不多, 只不过类方法需要在参数列表中的第一个位置预留一个位置, 通常我们给第一个参数起名字叫cls. 类方法在被调用的时候也不需要传递实例对象. 但是. 系统会自动的把类传递给第一个参数. 类方法在编写的时候, 需要在类方法上面添加@classmethod
面试题: 类方法/静态方法和实例方法有什么区别?
类的成员-属性
属性其实就是通过方法改造过来的一种变量的写法, 在方法上添加一个@property就可以 了
应用场景: 我们一般保存数据的时候, 不会保存一个人的年龄. 因为随着时间的推移. 每个人的年龄都时刻在改变着. 那如何保存加完美呢? 很简单. 保存出⽣年⽉⽇. 然后用程序来 计算,你当前的年龄. 实时的. 那这个时候就需要相应的计算了. ⽽计算属于一个功能. 当然要写方法里了. 但是对于年龄这个属性⽽言. 他应该是一个数值. ⽽不是动作. 所以python 就提供了这样一种机制. 通过方法来描述一个属性.
注意:
1. 方法参数只能有一个self
2. 方法上要写@property
3. 调用的时候, 我们不需要写括号. 直接当成属性变量来用就可以了.
4. 这种套路只能取值. 不能设置值
四. 私有
在python中, 一般是不提倡设置和使用私有信息的. 但有些场景, 我们不得不这么做
在python中使用__作为⽅法或者变量的前缀. 那么这个方法或者变量就是一个私有的.(后缀没有_)
私有的内容不能直接访问. 但是如果对方开辟了外界访问的通道(公共方法). 那可以 通过这个公共的⽅法来获取到私有的内容.
类变量(静态变量)一样拥有这样的属性:
2. 私有方法
私有方法, 顾名思义, 只能⾃己访问的方法. 别人都不能随便调用的
需要注意的是, 对于私有的内容⽽言. 子类是无法继承的.
类与类之间的关系:
1. 依赖关系
在方法中给方法传递一个对象. 此时类与类之间的关系是最轻的
2. 关联关系.组合关系, 聚合关系
其实这三个在代码上写法是一样的. 但是, 从含义上是不一样的.
1. 关联关系. 两种事物必须是互相关联的. 但是在某些特殊情况下是可以更改和更换的.
2. 聚合关系. 属于关联关系中的一种特例. 侧重点是xxx和xxx聚合成xxx
3. 组合关系. 属于关联关系中的一种特例. 写法上差不多. 组合关系比聚合还要紧密. 比 如人的大脑, 心脏, 各个器官. 这些器官组合成一个人. 这时. 人如果挂了. 其他的东西 也跟着挂了
继承关系
子类在不 影响父类的程序运行的基础上对父类进行的扩充和扩展. 这里.我们可以把父类被称为超类或者基类. 子类被称为派生类.
self在访问方法的顺序: 永远先找⾃己的. ⾃己的找不到再找父类的.
self就是你访问方法的那个对象. 先找⾃己, 然后在找父类
类的特殊成员:.
__init_()就是一个特殊的成员. 说白了. 带双下划线的那一坨. 这些方 法在特殊的场景的时候会被自动的执行,如:
1. 类名() 会自动执行__init__()
2. 对象() 会自动执行__call__()
3. 对象[key] 会自动执行__getitem__()
4. 对象[key] = value 会自动执行__setitem__()
5. del 对象[key] 会自动执行 __delitem__()
6. 对象+对象 会自动执行 __add__()
7. with 对象 as 变量 会自动执行__enter__ 和__exit__
8. 打印对象的时候 自动执行 __str__
9. 干掉可哈希 __hash__ == None 对象就不可哈希了.
等等
创建对象的真正步骤:
首先, 在执行类名()的时候. 系统会自动先执行__new__()来开辟内存. 此时新开辟出来的内 存区域是空的. 紧随其后, 系统自动调用__init__()来完成对象的初始化工作. 按照时间轴来算.
1. 加载类
2. 开辟内存(__new__)
3. 初始化(__init__)
4. 使用对象干xxxxxxxxx
一. isinstance, type, issubclass
先看issubclass() 这个内置函数可以帮我们判断xxx类是否是yyy类型的子类.
1 class Base:
2 pass
3 class Foo(Base):
4 pass
5 class Bar(Foo):
6 pass
7 print(issubclass(Bar, Foo)) # True
8 print(issubclass(Foo, Bar)) # False
9 print(issubclass(Bar, Base)) # True
type(obj) 表示查看obj是由哪个类创建的.
可以帮我们判断xxx是否是xxx数据类型的
type 获取到xxx对象的类型
isinstance也可以判断xxx是yyy类型的数据. 但是isinstance没有type那么精准.
isinstance可以判断该对象是否是xxx家族体系中的(只能往上判断)
如何判断一个方法或者一个函数(FunctionType, MethodType)
from types import FunctionType, MethodType
print(isinstance(xx, FunctionType)))
print(isinstance(xx, MethodType)))
二、区分方法和函数
1. 类方法. 不论任何情况, 都是方法.
2. 静态方法, 不论任何情况. 都是函数
3. 实例方法, 如果是实例(对象)访问. 就是方法. 如果是类名访问就是函数.
三、反射
两个函数. 一个是getattr(). 一个是hasattr(). 其中getattr()用来获 取信息. hasattr()用来判断xxx中是否包含了了xxx功能,
那么我们可以在模块中这样来使用反射. 在面向对象中一样可以这样进行操作.
getattr(对象, 字符串): 从对象中获取到xxx功能. 此时xxx是一个字符串. get表示找, attr 表⽰属性(功能).
补充:
关于反射, 其实一共有4个函数:
1. hasattr(obj, str) 判断obj中是否包含str成员
2. getattr(obj,str) 从obj中获取str成员
3. setattr(obj, str, value) 把obj中的str成员设置成value. 注意. 这里的value可以是值, 也可以是函数或者方法
4. delattr(obj, str) 把obj中的str成员删除掉
注意, 以上操作都是在内存中进行的. 并不会影响你的源代码
约束
1. 约束(1. 抛出异常, 2. 写抽象类)
1. 抛出异常; NotImplementedError
1 class Base:
2 def login(self):
3 raise Exception("你没有实现login⽅方法()")
4 class Normal(Base):
5 def login(self):
6 pass
7 class Member(Base):
8 def denglu(self):
9 pass
10 class Admin(Base):
11 def login(self):
12 pass
13 # 项目经理理写的总入口
14 def login(obj):
15 print("准备验证码.......")
16 obj.login()
17 print("进⼊入主⻚页.......")
18 n = Normal()
19 m = Member()
20 a = Admin()
21 login(n)
22 login(m) # 报错.
23 login(a)
2. 抽象类
from abc import ABCMeta, abstractmethod
class Base(metaclass = ABCMeta): 抽象类
@abstractmethod
def 方法(self):pass
class Foo(Base): 子类必须重写父类中的抽象方法
def 方法(self):
pass
1 from abc import ABCMeta, abstractmethod
2 class Base(metaclass=ABCMeta):
3 @abstractmethod
4 def login(self):
5 pass
6 class Normal(Base):
7 def login(self):
8 pass
9 class Member(Base):
10 def denglu(self): # 这个就没用了
11 pass
12 def login(self): # ⼦类对父类进行实现
13 pass
14 class Admin(Base):
15 def login(self):
16 pass
17 # 项⽬目经理理写的总⼊入⼝口
18 def login(obj):
19 print("准备验证码.......")
20 obj.login()
21 print("进⼊入主⻚页.......")
22 n = Normal()
23 m = Member()
24 a = Admin()
25 login(n)
26 login(m)
27 login(a)
一个类包含类抽象方法. 这个类一定是抽象类
抽象类中可以有正常的方法
抽象类中如果有抽象方法. 这个类将不能创建对象
接口: 类中都是抽象方法
2. 异常处理
try:
except Error as e:
except....
else:
finally:
收尾
import traceback
try:
# 尝试执行的代码
except Exception as e:
# 除了错之后要做什么
要引入一个模块traceback. 这个模块可以获取到我们每个方法的调用信息. 又被成为堆栈信息
traceback.format_exc() # 获取堆栈信息(错误信息)
3.md5加密
MD5是一种不可逆的加密算法
1 import hashlib
2 def my_md5(s):
3 obj = hashlib.md5(b"fjlksajflkjasfsalwer123dfskjf")
4 obj.update(s.encode("utf-8")) # 加密的必须是字节
5 miwen = obj.hexdigest()
6 return miwen
4. 日志处理
logging
critical
error(最多)
wraning
info
debug.
1. 导入logging模块.
2. 简单配置⼀一下logging
3. 出现异常的时候(except). 向日志里写错误信息.
# filename: 文件名
# format: 数据的格式化输出. 最终在日志文件中的样⼦子
# 时间-名称-级别-模块: 错误信息
# datefmt: 时间的格式
# level: 错误的级别权重, 当错误的级别权重大于等于leval的时候才会写⼊文件
logging.basicConfig(filename='x1.txt', format='%(asctime)s - %(name)s - %(levelname)s -% (module)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S', level=10)# 当前配置表示 10以上的分数会被写⼊入⽂文件
# CRITICAL = 50
# FATAL = CRITICAL
# ERROR = 40
# WARNING = 30
# WARN = WARNING
# INFO = 20
# DEBUG = 10
# NOTSET = 0
logging.critical("我是critical") # 50分. 最贵的
logging.error("我是error") # 40分
logging.warning("我是警告") # 警告 30
logging.info("我是基本信息") # 20
logging.debug("我是调试") # 10
logging.log(2, "我是⾃自定义") # 自定义. 看着给分
日志的分开记录
import logging
# 创建一个操作日志的对象logger(依赖FileHandler)
file_handler = logging.FileHandler('l1.log', 'a', encoding='utf-8')
file_handler.setFormatter(logging.Formatter(fmt="%(asctime)s - %(name)s - % (levelname)s -%(module)s: %(message)s"))
logger1 = logging.Logger('s1', level=logging.ERROR)
logger1.addHandler(file_handler)
logger1.error('我是A系统')
# 再创建一个操作日志的对象logger(依赖FileHandler)
file_handler2 = logging.FileHandler('l2.log', 'a', encoding='utf-8')
file_handler2.setFormatter(logging.Formatter(fmt="%(asctime)s - %(name)s %(levelname)s -%(module)s: %(message)s"))
logger2 = logging.Logger('s2', level=logging.ERROR)
logger2.addHandler(file_handler2)
logger2.error('我是B系统')
1. 多继承
继承: x是一种y的时候.可以使用继承关系. 即"is a"关系
一个类同时继承多个类(python, c++)
2. 经典类的MRO
通过数型结构的深度优先遍历
一条道走到黑(从左往右)
3. 新式类的MRO(重点, 难点) C3算法
先拆分再合并 第一项的头和后面所有项的尾进行比较 如果出现了. 就拿出来 如果没出现. 下一项....
新式类通过__mro__可以直接看到MRO的结果
1 class A:
2 pass
3 class B(A):
4 pass
5 class C(A):
6 pass
7 class D(B, C):
8 pass
9 class E(C, A):
10 pass
11 class F(D, E):
12 pass
13 class M(F, E):
14 pass
15 class N:
16 pass
17 class P(M,N):
18 pass
19 class G(P):
20 pass
21 class O:
22 pass
23 class X(O):
24 pass
25 class H(G, X, F):
26 pass
27 print(H.__mro__)
28 '''
29 L(H) = H + L(G) + L(X) + L(F) + GXF HGPMXFDBECANO
30
31 L(G) = G + L(P) + P # GPMFDBECAN
32 L(X) = X + L(O) + O # XO
33 L(F) = F + L(D) + L(E) + DE # FDBECA
34
35
36 L(P) = P + L(M) + L(N) + MN # PMFDBECAN
37 L(D) = D + L(B) + L(C) + BC # DBCA
38 L(E) = E + L(C) + L(A) + CA # ECA
39
40 L(M) = M + L(F) + L(E) + FE # ECA ECA E MFDBECA
41
42 '''
4.super()
super() 找MRO顺序的下一个
super()可以帮我们执行MRO中下一个父类的方法. 通常super()有两个使用的地方:
1. 可以访问父类的构造⽅方法
2. 当子类⽅法想调用父类(MRO)中的方法
结论: 不管super()写在哪儿. 在哪儿执行. 一定先找到MRO列表. 根据 MRO列表的顺序往下找. 否则一切都是错的!