1、类和对象
1.1 基本概念
类: 用来描述具有相同的属性和方法的对象的集合—>车类
对象: 通过类定义的数据结构实例—>我的车
属性: 对象的描述信息---->车的零部件
方法: 对象的行为—>车的功能(将零部件组装,实现某种功能)
1.2 常见的三种编程范式
- 函数式编程
高阶函数、返回函数、装饰器等 - 面向过程编程(函数)
着重于作什么
函数封装,实现复用 - 面向对象编程(类)
着重于谁去做
把 变量和函数 再做1层封装
·在类中对数据的赋值、内部调用对外部用户是透明的。
·把一些功能的实现细节不对外暴露
方便版本迭代更新, 程序结构清晰明了
- 代码重用,实现这种重用的方法之一是通过继承机制
- 可以节省很多的代码,不需要写,直接使用
- 衍生出不同的子类,大部分代码一样,部分代码不一样
封装 隐藏对象的属性和实现细节,仅对外提供公共访问方式。
在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、类的定义和使用
文章图片
类空间和实例空间
类创建的时候会生成类空间
实例化对象的时候会生成实例空间,不同的实例空间都是独立的
实例查找属性方法的时候,先在自己的实例空间找,找不到就去类空间查找
类空间找不到就去父类找,一层一层往上找
为什么实例可以访问类属性
创建实例的时候,会有一个类对象指针,通过这个指针,实例就能访问类的属性和方法了
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()
文章图片
经典类: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进制地址
__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
推荐阅读
- python笔记|Python迭代器与生成器
- #|数据分析与可视化(四)Pandas学习基础一(统计分析基础)
- 数据分析|机器学习多元线性回归模型(公式推导以及代码实现)
- 机器学习|机器学习(2)-朴素贝叶斯的理解和代码实现
- numpy|numpy库的使用-读取数据
- python|机器学习--朴素贝叶斯分类器(python手动实现)
- python|python-pandas dataframe正则筛选包含某字符串的列数据str.contains()
- go语言专栏|如何解决GoLand2021版取消Go Module 以及Go get 因被墙下载gin失败
- Python全栈系列[更新中]|Python零基础入门篇 - 53 - 文件的读取