知识总结和记录——面向对象
面向过程 VS 面向对象
面向过程的程序设计的核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西。
优点是:极大的降低了写程序的复杂度,只需要顺着要执行的步骤,堆叠代码即可。
缺点是:一套流水线或者流程就是用来解决一个问题,代码牵一发而动全身。
面向对象的程序设计的核心是对象(上帝式思维),要理解对象为何物,必须把自己当成上帝,上帝眼里世间存在的万物皆为对象,不存在的也可以创造出来。
优点是:解决了程序的扩展性。对某一个对象单独修改,会立刻反映到整个体系中,如对游戏中一个人物参数的特征和技能修改都很容易。
缺点:可控性差,无法向面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题,即便是上帝也无法预测最终结果。于是我们经常看到一个游戏人某一参数的修改极有可能导致阴霸的技能出现,一刀砍死3个人,这个游戏就失去平衡。
类和对象
在python中,用变量表示特征,用函数表示技能,因而具有相同特征和技能的一类事物就是‘类’,对象是则是这一类事物中具体的一个。
类有两种作用:属性引用和实例化
属性引用(类名.属性)
实例化:类名加括号就是实例化,会自动触发__init__函数的运行,可以用它来为每个实例定制自己的特征
关于self
构造函数中的self表示创建的当前对象。
方法中的self表示谁调用的方法,self就是谁。
类属性的补充
一:我们定义的类的属性到底存到哪里了?有两种方式查看 dir(类名):查出的是一个名字列表 类名.__dict__:查出的是一个字典,key为属性名,value为属性值 二:特殊的类属性 类名.__name__# 类的名字(字符串) 类名.__doc__# 类的文档字符串 类名.__base__# 类的第一个父类(在讲继承时会讲) 类名.__bases__# 类所有父类构成的元组(在讲继承时会讲) 类名.__dict__# 类的字典属性 类名.__module__# 类定义所在的模块 类名.__class__# 实例对应的类(仅新式类中)
对象的相关知识
对象是关于类而实际存在的一个例子,即实例
对象/实例只有一种作用:属性引用
面向对象小结——定义及调用的固定模式
class 类名: def __init__(self,参数1,参数2): self.对象的属性1 = 参数1 self.对象的属性2 = 参数2 def 方法名(self):pass def 方法名2(self):pass 对象名 = 类名(1,2) #对象就是实例,代表一个具体的东西 #类名() : 类名+括号就是实例化一个类,相当于调用了__init__方法 #括号里传参数,参数不需要传self,其他与init中的形参一一对应 #结果返回一个对象 对象名.对象的属性1 #查看对象的属性,直接用 对象名.属性名 即可 对象名.方法名() #调用类中的方法,直接用 对象名.方法名() 即可
实例方法、类方法和静态方法
class A: explanation = "this is 98K" # 普通方法 def normalMethod(self, name): print(self.explanation) # 类方法,可访问类属性 @classmethod def classMethod(cls, explanation): print(cls.explanation) # 静态方法,不可访问类属性 @staticmethod def staticMethod(explanation): print(explanation)
实例方法(普通方法)——随着实例属性的改变而改变
类方法(无论是类调用还是实例调用)——都是类的属性值,不随实例属性的变化而变化
静态方法——不可访问类属性,故直接输入传入方法的值(当类中的方法不需要传入实例变量的时候,就可以使用静态方法)
属性方法
属性方法其实就是把类中的方法改造成属性的一种写法,该写法需要在方法上加上@property
from datetime import datetime from dateutil.relativedelta import relativedelta class Person: def __init__(self, name, birthday): self.name = name self.birthday = birthday @property def age(self): # 人的年龄是一个属性,但是需要由生日和当前时间计算得出 # 所以我们把这个方法包装成属性 ret = relativedelta(datetime.now(), datetime.strptime(self.birthday, '%Y-%m-%d')) return ret.years p1 = Person('李国庆', '1990-10-01') # 像访问实例属性一样 print(p1.age)
私有方法和私有变量(加入双下线__来表示,私有成员子类无法继承)
反射
通过字符串动态的访问模块(对象)中的功能和属性
使用反射的四个函数:
- hasattr(obj, str) 判断obj中是否包含str成员
- getattr(obj,str) 从obj中获取str成员。
- setattr(obj, str, value) 把obj中的str成员设置成value。这⾥的value可以是值,也可以是函数或者⽅法。
- delattr(obj, str) 把obj中的str成员删除掉。
class Foo: pass f = Foo() print(hasattr(f, 'eat')) # False setattr(f, 'eat', "123") print(f.eat) # 被添加了⼀个属性信息 setattr(f, "eat", lambda x: x + 1) print(f.eat(3)) # 4 print(f.eat) # 此时的eat既不是静态⽅法, 也不是实例⽅法, 更不是类⽅法. 就相当于你在类中写了个self.chi = lambda 是⼀样的 print(f.__dict__) # {'eat': <function <lambda> at 0x1015a2048>} delattr(f, "eat") print(hasattr(f, "eat")) # False
importlib
importlib是一个可以根据字符串的模块名实现动态导入模块的库(Django中间件会使用到)
类的其他成员
__str__ , __repr__ 使用print打印一个对象时,后调用 class Student(): def __init__(self,name): self.name = name def __str__(self): # __repr__ return self.name s = Student('Alex') print(s) __format__ 格式化,没怎么用过 __del__ 手动垃圾回收,一般没卵用 __new__ 类实例被创建后调用的是__init__,而之前是__new__方法创建这个实例类的方法 __call__ 方法的执行是由对象后加括号触发的,即:对象()。拥有此方法的对象可以像函数一样被调用。 class Person: def __init__(self, name, age): self.name = name self.age = age def __call__(self, *args, **kwargs): print('调用对象的__call__方法') a = Person('张三', 24) # 类Person可调用 a() # 对象a可以调用 __doc__ 打印类的描述信息 class A(): """我是描述信息""" pass print(A.__doc__) 还有__len__、 __hash__、__enter__ 、__exit__等等方法
super()
在子类中调用父类的方法使用super()
super()函数的一种常见用途是调用父类的__init__()方法,以确保父类被正确的初始化了
class A: def __init__(self): self.x = 0 class B(A): def __init__(self): # 先调用父类的__init__()方法 super().__init__() # 执行自己的初始化操作 self.y = 1 b = B() print(b.x) print(b.y)
面向对象的组合用法
软件重用的重要方式除了继承之外还有另外一种方式,即:组合
组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合
class Weapon: def prick(self, obj): # 这是该装备的主动技能,扎死对方 obj.life_value -= 500 # 假设攻击力是500 class Person: # 定义一个人类 role = 'person' # 人的角色属性都是人 def __init__(self, name): self.name = name # 每一个角色都有自己的昵称; self.weapon = Weapon() # 给角色绑定一个武器; egg = Person('egon') egg.weapon.prick()
用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教python课程
class BirthDate: def __init__(self,year,month,day): self.year=year self.month=month self.day=day class Couse: def __init__(self,name,price,period): self.name=name self.price=price self.period=period class Teacher: def __init__(self,name,gender,birth,course): self.name=name self.gender=gender self.birth=birth self.course=course def teach(self): print('teaching') p1=Teacher('egon','male', BirthDate('1995','1','27'), Couse('python','28000','4 months') ) print(p1.birth.year,p1.birth.month,p1.birth.day) print(p1.course.name,p1.course.price,p1.course.period) ''' 运行结果: 1 27 python 28000 4 months '''
接口类(使用abc实现接口格式统一)
from abc import abstractmethod,ABCMeta class Paymethod(metaclass=ABCMeta): @abstractmethod def pay(self, money): pass class WechatPay(Paymethod): def pay(self, money): print('微信支付了%s'%money) class QQPay(Paymethod): def pay(self, money): print('qq支付了%s'%money) def pay(obj, money): obj.pay(money) we = WechatPay() qq = QQPay() pay(we,200) pay(qq,100)
抽象类
#一切皆文件 import abc #利用abc模块实现抽象类 class All_file(metaclass=abc.ABCMeta): all_type='file' @abc.abstractmethod #定义抽象方法,无需实现功能 def read(self): '子类必须定义读功能' pass @abc.abstractmethod #定义抽象方法,无需实现功能 def write(self): '子类必须定义写功能' pass # class Txt(All_file): # pass # # t1=Txt() #报错,子类没有定义抽象方法 class Txt(All_file): #子类继承抽象类,但是必须定义read和write方法 def read(self): print('文本数据的读取方法') def write(self): print('文本数据的读取方法') class Sata(All_file): #子类继承抽象类,但是必须定义read和write方法 def read(self): print('硬盘数据的读取方法') def write(self): print('硬盘数据的读取方法') class Process(All_file): #子类继承抽象类,但是必须定义read和write方法 def read(self): print('进程数据的读取方法') def write(self): print('进程数据的读取方法') wenbenwenjian=Txt() yingpanwenjian=Sata() jinchengwenjian=Process() #这样大家都是被归一化了,也就是一切皆文件的思想 wenbenwenjian.read() yingpanwenjian.write() jinchengwenjian.read() print(wenbenwenjian.all_type) print(yingpanwenjian.all_type) print(jinchengwenjian.all_type)
多继承
python3为新式类,按照广度优先原则查找,父类继承object
class A:
def __init__(self):
print('进入A类')
print('离开A类')
class B(A):
def __init__(self):
print('进入B类')
super().__init__()
print('离开B类')
class C(A):
def __init__(self):
print('进入C类')
super().__init__()
print('离开C类')
class D(B, C):
def __init__(self):
print('进入D类')
super().__init__()
print('离开D类')
实例化结果:
进入D类 进入B类 进入C类 进入A类 离开A类 离开C类 离开B类 离开D类
- 子类永远在父类前面
- 如果有多个父类,会根据它们在列表中的顺序被检查
- 如果对下一个类存在两个合法的选择,选择第一个父类
super()函数所做的事情就是在MRO列表中找到当前类的下一个类