python之面向对象
一.初识面向对象
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算法