一.初识面向对象
1.类与对象的关系: 类是对事物的总结. 抽象的概念. 类用来描述对象. 对象是类的实例化的结果. 对象能执行哪些方法. 都由类来决定. 类中定义了什么. 对象就拥有什么
class 类名:
方法
类() #对象
class Car:
pass
Car()
- 注意:类名的首字母大写,严格遵守驼峰命名规范
1.1python中我们可以是用__init__(self)函数给对象进行初始化操作. 这个函数(方法)被称为构造函数(方法)或初始化方法
- 在创建对象的时候会自动的调用 __init__()
- self是你创建出来的那个对象
class Car: #创建类
def __init__(self,color)
self.color=color#属性
c = Car() #创建对象
1.2 方法 ->在类中的函数
class Phone: #创建类
def __init__(self,color,price): #初始化方法(构造方法) 自动调用
self.color=color #属性
self.price=price #属性
def call(self,man): #方法
print(f'用我{self.color}手机给{man}打电话')
def play(self): #方法
print('我%s的手机可以打游戏' % self.price)
p = Phone('红色',5000)
p.call('马云')
p.play()
2.面向对象和面向过程的对比
2.1⾯向过程: 一切以事物的流程为核心. 核心是"过程"二字, 过程是指解决问题的步骤,即, 先干什么, 后干什么.
- 优点: 负责的问题流程化, 编写相对简单
- 缺点: 可扩展性差
2.2面向对象: 一切以对象为中心.
- 优点: 可扩展性强
- 缺点: 编程的复杂度高于⾯面向过程
3.面向对象的三个特征
- ⾯向对象三大特征: 封装, 继承, 多态. 只要是面向对象编程语言. 都有这三个特征.
3.1. 封装: 把很多数据封装到一个对象中. 把固定功能的代码封装到一个代码块, 函数, 对象, 打包成模块. 这都属于封装的思想.
3.2. 继承: 子类可以自动拥有父类中除了私有属性外的其他所有内容. 在python中实现继承非常简单. 在声明类的时候, 在类名后面添加一个小括号,就可以完成继承关系. 那么什么情况可以使用继承呢? 两个类具有相同的功能或者特征的时候. 可以采用继承的形式. 提取一个父类, 这个父类中编写着两个类相同的部分. 然后两个类分别取继承这个类就可以了了.
- 这样写的好处是我们可以避免写很多重复的功能和代码. 如果从语义中去分析的话. 会简单很多. 如果语境中出现了x是一种y. 这时, y是⼀一种泛化的概念. x比y更加具体. 那这时x就是y的子类.
3.3. 多态: 同一个对象, 多种形态.
- 多态的好处: 程序具有超⾼高的可扩展性. ⾯面向对象思想的核⼼心与灵魂.
二.类的成员
什么是类的成员? 你能在类中写什么? 写的内容就是成员.
class 类名:
# 方法
def __init__(self, 参数1, 参数2....):
# 属性变量
self.属性1 = 参数1
self.属性2 = 参数2
....
# 方法
def method(self):
pass
1.类的成员------变量
1.1.实例变量,类变量(静态变量)
- 实例变量是给对象用的,
- 类变量对多个对象共享,最好用类名来访问
class Person:
country = "中国" # 类变量, 表示所有的该类的对象都共享这个变量.
def __init__(self, name, num, birthday):
# 实例变量(字段) 表示你创建的每⼀个人都有这三个变量量
self.name = name
self.num = num
self.birthday = birthday
p1 = Person("alex", 18, "1840年年06⽉月01⽇日")
print(p1.name)
print(p1.country) #中国
p2 = Person("wusir", 28, "1894年年07⽉月25⽇日")
print(p2.name)
print(p2.country) #中国
2.类的成员------方法
2.1.实例方法(成员方法) 对象直接访问的方法叫实例方法
class Computer:
def play(self): #实例方法 参数为self
print('电脑可以玩')
c = Computer()
c.play() #对象直接调用实例方法
2.2.类方法 @classmethod
类方法需要在参数列表中的第一个位置预留一个位置, 通常我们给第一个参数起名字叫cls. 类方法在被调用的时候也不需要传递实例对象.但是. 系统会自动的把类传递给第一个参数. 类方法在编写的时候, 需要在类方法上面添加@classmethod
class Phone:
@classmethod
def play(cls): #类方法 参数必须是cls
print('我的手机可以打游戏')
2.3.静态方法 @staticmethod
- 静态方法不需要我们给方法传递self. 也就是说. 当出现一个方法不需要使用到成员变量的时候. 就可以选择使用静态方法. 静态方法需要我们在方法上面添加一个@staticmethod
- 静态方法和静态变量一样. 一般都是使用类名直接访问和调⽤用的.
class Phone:
@staticmethod
def func(): #静态方法 没有参数
print('我的电话可以打电话')
3.类的成员------属性
属性其实就是通过方法改造过来的一种变量的写法, 在方法上添加⼀个@property就可以了
注意:
1. ⽅法参数只能有⼀个self
2. ⽅法上⽅要写@property
3. 调用的时候, 我们不需要写括号. 直接当成属性变量来用就可以了.
4. 这种套路只能取值. 不能设置值
class Person:
def __init__(self):
pass
@property
def age(self):
return 1
p = Person()
age = p.age
print(age)
4.私有方法和私有变量
在python中使用__作为⽅法或者变量的前缀. 那么这个方法或者变量就是一个私有的.
class Person:
def __init__(self, mimi):# 私有变量
self.__mimi = mimi
def __age(self):#私有方法
print("不告诉你")`
三.类与类之间的关系
1.依赖关系
class Elphant:
def __init__(self, name):
self.name = name
def open(self):
'''
开门
:return:
'''
pass
def close(self):
'''
关门
:return:
'''
pass
class Refrigerator:
def open_door(self):
print("冰箱门被打开了")
def close_door(self):
print("冰箱门被关上了")
#大象和冰箱之间就是依赖关系. 我用着你. 但是你不属于我. 这种关系是最弱的.
2.关联关系,组合关系,聚合关系
其实这三个在代码上写法是一样的. 但是, 从含义上是不一样的.
-
关联关系. 两种事物必须是互相关联的. 但是在某些特殊情况下是可以更改和更换的.
-
聚合关系. 属于关联关系中的一种特例. 侧重点是xxx和xxx聚合成xxx. 各自有各自的声明周期. 比如电脑. 电脑⾥里里有CPU, 硬盘, 内存等等. 电脑挂了了. CPU还是好的. 还是完整的个体
-
组合关系. 属于关联关系中的一种特例例. 写法上差不多. 组合关系比聚合还要紧密. 比如人的大脑, 心脏, 各个器官. 这些器官组合成一个人. 这时. 人如果挂了. 其他的东西也跟着挂了
3.继承关系
在面向对象的世界中存在着继承关系. 我们现实中也存在着这样的关系. 比如. x是一种y, 那x就可以继承y. 这是理解层面上的. 如果上升到代码层面. 我们可以这样认为. 子类在不影响父类的程序运行的基础上对父类进行的扩充和扩展. 这里.我们可以把父类被称为超类或者基类. 子类被称为派生类
class Base:
def __init__(self, num):
self.num = num
def func1(self):
print(self.num)
class Foo(Base):
def func1(self):
print("Foo. func1", self.num)
obj = Foo(123)
obj.func1() # Foo. func1 123 运行的是Foo中的func1
四.特殊成员
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
五.反射
1.isinstance type和issubclass的应用
- issubclass() 这个内置函数可以帮我们判断xxx类是否是yyy类型的子类.
class Base:
pass
class Foo(Base):
pass
class Bar(Foo):
pass
print(issubclass(Bar, Foo)) # True
print(issubclass(Foo, Bar)) # False
print(issubclass(Bar, Base)) # True
-
type(obj) 表示查看obj是由哪个类创建的.
class Foo:
pass
obj = Foo()
print(obj, type(obj)) # 查看obj的类
-
isinstance也可以判断xxx是yyy类型的数据.但是isinstance没有type那么精准.
-
isinstance可以判断该对象是否是xxx家族体系中的(只能往上判断)
class Base:
pass
class Foo(Base):
pass
class Bar(Foo):
pass
print(isinstance(Foo(), Foo)) # True
print(isinstance(Foo(), Base)) # True
print(isinstance(Foo(), Bar)) # False
2.函数和方法的区分
- 函数在打印的时候. 显示的是function. 而方法在打印的时候是method.
class Foo:
def chi(self):
print("我是吃")
@staticmethod
def static_method():
pass
@classmethod
def class_method(cls):
pass
f = Foo()
print(f.chi) # <bound method Foo.chi of <__main__.Foo object at 0x10f688550>>
print(Foo.chi) # <function Foo.chi at 0x10e24a488>
print(Foo.static_method) # <function Foo.static_method at 0x10b5fe620>
print(Foo.class_method) # bound method Foo.class_method of <class '__main__.Foo'>>
print(f.static_method) # <function Foo.static_method at 0x10e1c0620>
print(f.class_method) #<bound method Foo.class_method of <class '__main__.Foo'>>
仔细观察, 我们能得到以下结论:
- 1. 类方法. 不论任何情况, 都是方法.
- 2. 静态方法, 不论任何情况. 都是函数
- 3. 实例方法, 如果是实例访问. 就是方法. 如果是类名访问就是函数.
3.反射
关于反射, 其实一共有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成员删除掉
六.约束,异常抛出,异常处理,MD5和日志的处理
1.约束
2.异常抛出
3.日常处理
4.MD5加密算法
5.日志
import logging
# 配置好日志的处理, 默认就是GBK
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以上的分数会被写入日件
# 向日志文件写入内容
logging.critical("最高的") # 50, 几乎是最高的
logging.error("最多的") # 40 平时使用最多的就是它
logging.warn("警告") # 30 警告
logging.warning("警告")
logging.info("提示") # 20 级
logging.debug("最低的") # 10
logging.log(999, "自己定义一个等级") #自己定义一个等级
# 再创建⼀个操作⽇志的对象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('百度贴吧', level=logging.DEBUG)
logger2.addHandler(file_handler2)
logger2.error("我才不去呢. 我们在北京. 离你那么远")
import traceback
class GenderException(Exception):
pass
class Person:
def __init__(self, name, gender):
self.name = name
self.gender = gender
logger1.info(f"这个人的名字是{self.name}, 这个人的性别是:{self.gender}")
def xizao(self):
print(f"{self.name}在洗澡")
class ZaoTang:
def nan(self, ren):
if ren.gender == "男":
ren.xizao()
else:
raise GenderException("我这里要的是男人")
def nv(self, ren):
if ren.gender == "女":
ren.xizao()
else:
raise GenderException("我这里要的是女人")
try:
p1 = Person("赵亚磊", "男")
p2 = Person("林志玲", "女")
zaotang = ZaoTang()
zaotang.nan(p2)
zaotang.nv(p1)
except GenderException:
print("走错屋里了")
logger1.error("走错屋了.. ")
logger1.error(traceback.format_exc()) # 把堆栈信息记录在日志文件中
七.MRO C3算法