python笔记|Python 面向对象(一)

1、类和对象 1.1 基本概念
类: 用来描述具有相同的属性和方法的对象的集合—>车类
对象: 通过类定义的数据结构实例—>我的车
属性: 对象的描述信息---->车的零部件
方法: 对象的行为—>车的功能(将零部件组装,实现某种功能)
1.2 常见的三种编程范式

  • 函数式编程
    高阶函数、返回函数、装饰器等
  • 面向过程编程(函数)
    着重于作什么
    函数封装,实现复用
  • 面向对象编程(类)
    着重于谁去做
    把 变量和函数 再做1层封装
    ·在类中对数据的赋值、内部调用对外部用户是透明的。
    ·把一些功能的实现细节不对外暴露
1.3 为什么要面向对象
方便版本迭代更新, 程序结构清晰明了
  • 代码重用,实现这种重用的方法之一是通过继承机制
  • 可以节省很多的代码,不需要写,直接使用
  • 衍生出不同的子类,大部分代码一样,部分代码不一样
1.4 类的基本特征
封装 隐藏对象的属性和实现细节,仅对外提供公共访问方式。
在python中用双下划线开头的方式将属性设置成私有的。
好处:1. 将变化隔离;2. 便于使用;3. 提高复用性;4. 提高安全性。
继承 代码的重用
class Animal: species = 'Animal' count = 0def __init__(self): self.name = "animal" Animal.count += 1 print("初始化Animal")def breath(self): print("i can breath")def eat(self): print('i can eat')class Person(Animal): species = 'Person' # 重写了父类的属性class Dog(Animal): def __init__(self): print("i am dog")def eat(self): print("dog is eating")class Pig(Animal): count = 0def __init__(self): super().__init__()# 子类访问父类的init方法,建议放在最前面,否则跟父类的属性冲突了会覆盖 self.name = "pig" Pig.count += 1 print("初始化pig。。。。")print("实例化Animal".center(50, '*')) animal = Animal() print(animal.count) print("实例化Person".center(50, '*')) person = Person() print(person.count, person.species, Person.count) # 对象的属性查找,先去对象实例空间查找,没有就去类空间查找 # 类空间没有就去父类空间查找,层层递归往上查找print("实例化dog".center(50, '*')) d = Dog() d.eat()print("实例化pig".center(50, '*')) pig = Pig() print(Animal.count, pig.count, pig.name)

多态 实现接口的重用
python里不支持多态,python处处是多态
不支持多态是语法上的多态,不需要额外的实现多态的代码
按照多态语法来说,不属于多态(父类作为参数,传递子类对象)
python 里处处是多态,本身实现了多态
class Alipay(): def pay(self): print("支付宝的pay")class WeChat(): def pay(self): print("微信的pay")class WangYi(): def pay(self): print("WangYi的pay")zhi = Alipay() wei = WeChat( wang = WangYi() def paymethod(obj):# 不关心是什么类,只关心有没有pay()这个行为 obj.pay()paymethod(wang) paymethod(zhi)

2、类的定义和使用 python笔记|Python 面向对象(一)
文章图片

类空间和实例空间
类创建的时候会生成类空间
实例化对象的时候会生成实例空间,不同的实例空间都是独立的
实例查找属性方法的时候,先在自己的实例空间找,找不到就去类空间查找
类空间找不到就去父类找,一层一层往上找
为什么实例可以访问类属性
创建实例的时候,会有一个类对象指针,通过这个指针,实例就能访问类的属性和方法了
3、__ init__方法 初始化实例的方法 (静态方法)
实例对象的构造方法(初始化方法)
实例化对象的时候会自动调用__init__ 方法
4、__ new__ 方法 创建实例的方法(实例方法)
没有的话默认会继承object类的new方法,一般不需要重写
总结:
__ new__是创建实例的方法
__ init__ 对创建的实例进行初始化工作的方法
__ new__方法必须要传入一个参数(cls),代表当前类
__ init__ 必须要返回一个实例化对象
__ init__ 的self就表示__new__方法返回的实例,__ init__ 就对这个实例进行初始化
子类没有定于__ new__ 就会去找父类的__ new__
新式类才有 __ new__
如果实例化对象和本身class不一致,__ init__就不会执行
单例模式 无论实例化多少次,都只会返回同一个实例对象
通过重写new方法实现
class Student: obj = None def __new__(cls, *args, **kwargs): if cls.obj: cls = object.__new__(cls) return cls.obja = Student() b = Student() c = Student() print(id(a), id(b), id(c))

5、self详解 self代表类的实例,而非类
实例方法里面,第一个参数就代表实例本身,所以不一定非写成self
如果要通过实例调用,第一个参数必须要传,因为实例调用底层实现是Person.info( p ), p是必传参数
可以Peson.info()方法调用
class Person: name = "hejin" def info(self): print(f'i am {self.name}') print(self) print(type(self))p = Person() p.info()# 底层解释器 Person.info(p),因为实例空间是没有info方法, # 执行类里面的Person.info(p)是这里的p 就代表实例本身

6、类和实例 关系判断
print(isinstance(person,Person),isinstance(person,Animal)) #True True判断是否是类的实例 print(type(person)) # 只能判断当前类 print(Pig.__base__) # 查找父类

6.1 使用类实现迭代器
class Iterator1(): def __init__(self, it): self.index = 0 self.it = itdef __iter__(self): return selfdef __next__(self): if self.index >= len(self.it): print("迭代结束") else: self.index += 1 return self.it[self.index - 1] lst = [1, 2, 3, 4, 5, 6] str1 = "abcdef" itera = Iterator1(str1) print(itera.__next__()) print(itera.__next__())

6.2 使用类实现斐波拉契
class Fibonacci(): def __init__(self): self.a = 0 self.b = 1 def __iter__(self): return self def __next__(self): self.a, self.b = self.b, self.a + self.b return self.af = Fibonacci() for i in range(9): print(f.__next__(), end=' ')

7、经典类/新式类 python2—经典类、新式类
只有显示地继承了object的类称为新式类,其他都是经典类
继承了python内置类的类称为新式类,其他都是经典类
python3—新式类
默认都是继承object,所有都是新式类
#类的定义 class A(): pass class B: pass class C(object): pass# 在python2种 A、B 两类都属于经典类,只有C是新式类 # 在python3中 默认会继承object类,所有A、B、C都是新式类

经典类和新式类的最大区别就是有没有继承object类,还有继承顺序的区别
type查看
>>> class A:pass# Python2 ... >>> a = A() >>> type(a)#通过type查看到的实例类型都叫做instance >>> a.__class__ # 实例和类之间只能通过__class__ 属性进行关联

>>> class A:pass# Python3 ... >>> a = A() >>> type(a)#通过type查看到的实例类型就是类名 >>> a.__class__

继承顺序
class A(): def test(self): print("from A")class B(A): def test(self): print("from B")class C(A): def test(self): print("from C")class D(B): def test(self): print("from D")class E(C): def test(self): print("from E")class F(D, E):# MOR def test(self): print("from F")f = F() f.test()

python笔记|Python 面向对象(一)
文章图片

经典类:F——D——B——A——E——C(深度优先)
新式类:F——D——B——E——C——A(c3 算法,非广度有限)
c3算法总结:
首先将自身类加入到本序列,然后对继承序列的元素依次判断;
若某元素不在其他序列或者它是所有继承序列的第一个,那么就把这个元素提取到本序列;
举例:
A ——【A,Object】
B ——【B,A,Object】
D —— 【D,B,A,Object】
C —— 【C,A,Object】
E ——【E,C,A,Object】
F ——【F】+【D,B,A,Object】+【E,C,A,Object】
最后变成:【F,D,B】 因为A在【E,C,A,Object】在这个序列里面,所以A丢掉
8、属性和方法 属性:普通/静态
静态属性:类属性
普通属性:实例属性
方法:实例/静态/类方法
class A(): name = '长沙'# 静态属性def __init__(self): self.country = "china"# 普通属性# 普通(实例)方法 # 接收的第一个参数,就代表实例本身 def normal_method(self, name): print("normal".center(20, '*')) print(self.name, name)# 使用classmethod修饰的方法,称为类方法 # 接收的第一个参数,就代表类本身 @classmethod def class_method(cls, name): print("class_method:".center(20, '*')) print(type(cls), cls) print(cls.name, name)# 访问不了实例属性# 使用staticmethod修饰的方法,称为静态方法 # 可以接收参数也可以不接,参数不代表实例和类本身 @staticmethod def static_method(name): print("static_method:".center(20, '*')) print(A.name, name)# 通过类名访问属性# 通过实例调用实例、静态、类方法 a = A() a.normal_method("实例调用实例方法") a.class_method("实例调用类方法") a.static_method("实例调用静态方法")# 通过类调用实例、静态、类方法 # 通过类调用实例方法的时候,一定要传一个实例进去 A.normal_method(a, "类调用实例方法") A.class_method("类调用类方法") A.static_method("类调用静态方法")

结果:
*******normal******* 长沙 实例调用实例方法 ***class_method:**** 长沙 实例调用类方法 ***static_method:*** 长沙 实例调用静态方法 *******normal******* 长沙 类调用实例方法 ***class_method:**** 长沙 类调用类方法 ***static_method:*** 长沙 类调用静态方法

查看类属性和方法
? dir(Person):了解Person类里的变量和方法
? dir( p ) 或 p.__dir__() :查看对象的属性和方法
? Person.__dict__ :可以了解哪些是变量哪些是方法
内置属性及其功能
查看文档注释
第一个用三引号引起来的字符串
print(Parent.__doc__)
查看帮助文档
print(help(Parent))
查看对象和类空间
print(p.__dict__) #类的属性(包含一个字典,由类的数据属性组成)
print(Parent.__dict__) # 了解哪些是变量哪些是方法
查看对象属于那个类
print(p.__class__)
print(Parent.__name__) 查看类名
查看父类
print(Child.__bases__) 查看 继承的所有父类
print(Child.__base__)
print(Parent.__base__)
print(Parent.__bases__)
查看对象的哈希值
print(Parent.__hash__)
查看类定义所在的模块
print(a.__module__)
print(A.__module__)
9、Python中的下划线 【python笔记|Python 面向对象(一)】不能被模糊导入
双下划线开头 只有类对象自己可以调用
class Parent: tmp = "tmp" _min = 1# 保护成员 __max = 10# 私有属性def __init__(self): self.name = 'hejin' self._age = 18 self.__desc = 'it' # 实例的私有属性,在类的内部调用def __make(self): print("这是一个私有方法") print(self.__desc)def _protectmake(self): print("这是一个保护方法")def show(self): print(self.__max, self.__desc)# 只能在类的内部调用 print("这是一个普通方法")class Child(Parent): def show(self): print(self.__max)p = Parent() c = Child() # print(p.tmp, c.tmp) # print(p._min, c._age, c._min) # p._protectmake() # c._protectmake()# print(p.__max) # p.show() # c.show()# 查看实例空间有哪些属性 print(p.__dict__)# self.__desc——》 _Parent__desc _类名__属性/方法名 # print(p.__desc) print(p._Parent__desc)# 伪私有,双下划线开头的标识符改了名字

10、常用魔术方法和属性 在Python中,所有以双下划线__包起来的方法,统称为Magic Method(魔术方法),它是一种的特殊方法,普通方法需要调用,而魔术方法不需要调用就可以自动执行。
魔术方法在类或对象的某些事件出发后会自动执行,让类具有神奇的“魔力”。如果希望根据自己的程序定制自己特殊功能的类,那么就需要对这些方法进行重写。
Python中常用的运算符、for循环、以及类操作等都是运行在魔术方法之上的
构造函数
__new__ :创建实例
__init__ :初始化实例
析构函数
__del__:在实例释放、销毁的时候自动执行的,通常用于做一些收尾工作, 如关闭一些数据库连接,关闭 打开的临时文件
class ATM: def __del__(self):# 析构函数再销毁之前需要做的事情 print("执行del") # 这里做的收尾工作def __call__(self, name, age): print(f"my name is {name},my age is {age}")a = ATM() del a# 关键字

调用方法
__call __ :把类实例化后的对象当做函数来调用的时候自动被调用
a("sc", 18) # 结果:my name is sc,my age is 18

其他魔术方法
  • __str__:给用户看的 ,返回对象的描述信息
    print(对象) 打印的结果就是调用repr
    没有定义__str__返回的就是__repr__
  • __repr__:更加官方的说明,给程序员的
    交互式环境里面直接实例名敲回车 返回的值就是调用的repr
    默认调用的是__repr__
    没有定义__repr__ 返回的是16进制地址
__getitem__:获取参数
__setitem__:设置数据
__delitem__:删除数据
class A: def __init__(self): self.data = https://www.it610.com/article/{}def __getitem__(self, key): print("get data:") return self.data.get(key, 0)def __setitem__(self, key, value): print("set data:") self.data[key] = valuedef __delitem__(self, key): print("delete data:") del (self.data[key])a = A() a["name"] = "hejin"#set data: print(a.data)#{'name': 'hejin'} print(a["name"])#get data:hejin del a["name"]#delete data: print(a.data)#{}

*? __eq__(self, other) 定义了等号的行为, ==
?__ne__(self, other) 定义了不等号的行为, !=
? __lt__(self, other) 定义了小于号的行为, <
? __gt__(self, other) 定义了大于等于号的行为, >=
?__add__(self, other) +运算
? __mul__(self, other) 运算
?__len__(self, other) 获得长度

练习:自定义异常
class LenError(Exception): def __init__(self, msg): self.msg = msg def __str__(self): return self.msglst = [1, 2, 3, 4, 5] try: if not 8 <= len(lst) <= 10: raise LenError("长度不在8-10之间") except LenError as er: print(er)

11、Python自省 在计算机编程中,自省是指这种能力:检查某些事物以确定它是什么、它知道什么以及它能做什么。自省向程序员提供了极大的灵活性和控制力。
自省有4个方法 :
  • hasattr(obj, ‘name’): 检查是否含有成员
    判断一个对象obj里是否有对应的name_str字符串的方法
print(hasattr(math, "xx"))# 判断math有没有xx属性 print(hasattr(math, "sin")) print(math.sin(90))

  • setattr(obj, ‘age’, 18): 设置成员
  • getattr(obj, ‘name’): 获取成员
    根据字符串去获取obj对象里的对应的方法的内存地址
  • delattr(obj, ‘name’): 删除成员
print(setattr(math, "xx", 1))# None 设置math的xx属性为1 print(getattr(math, "xx"))# 1获取math的xx属性 print(delattr(math, "xx"))# None 删除math的xx属性

import math class A: name = "sc" __max = "max"def f1(self): print("i am f1")a = A() print(hasattr(a, "name"))#True if hasattr(a, "f1"): print(getattr(a, "name")) #sc if hasattr(a, "f1"): print(getattr(a, "f1")())#i am f1 和Nonedef f2(): print("i am f2")setattr(a, "f3", f2) a.f3()#i am f2

    推荐阅读