Python面向对象 参考:Python 面向对象 | 菜鸟教程 (runoob.com)
面向对象简介:
类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
类变量: 类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
数据成员: 类变量或者实例变量, 用于处理类及其实例对象的相关的数据。
方法重写: 如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
局部变量: 定义在方法中的变量,只作用于当前实例的类。
实例变量: 在类的声明中,属性是用变量来表示的。这种变量就称为实例变量,是在类声明的内部但是在类的其他成员方法之外声明的。
继承: 即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟”是一个(is-a)”关系(例图,Dog是一个Animal)。
实例化: 创建一个类的实例,类的具体对象。
方法: 类中定义的函数。
对象: 通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法
创建类: 使用class语句来创建一个新类,class之后为类的名称并以冒号结尾:
1 2 3 class ClassName : '类的帮助信息' class_suite
类的帮助信息可以通过ClassName.__doc__查看。
class_suite 由类成员,方法,数据属性组成。
实例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Employee : '所有员工的基类' empCount = 0 def __init__ (self, name, salary ): self.name = name self.salary = salary Employee.empCount += 1 def displayCount (self ): print ("Total Employee %d" % Employee.empCount) def displayEmployee (self ): print ("Name : " , self.name, ", Salary: " , self.salary)
empCount 变量是一个类变量,它的值将在这个类的所有实例之间共享。你可以在内部类或外部类使用 Employee.empCount 访问。
第一种方法__init__()方法是一种特殊的方法,被称为类的构造函数或初始化方法,当创建了这个类的实例时就会调用该方法
self 代表类的实例,self 在定义类的方法时是必须有的,虽然在调用时不必传入相应的参数。
self代表类的实例,而非类 类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称 , 按照惯例它的名称是 self。
1 2 3 4 5 6 7 class Test : def prt (self ): print (self) print (self.__class__) t = Test() t.prt()
以上实例执行结果为:
1 2 <__main__.Test instance at 0x10d066878> __main__.Test
从执行结果可以很明显的看出,self 代表的是类的实例,代表当前对象的地址,而 self.class 则指向类。
注意:self并不是python关键字,将其换成其他字符串如piiick也可以正常执行。
创建实例对象 实例化类其他编程语言中一般用关键字 new,但是在 Python 中并没有这个关键字,类的实例化类似函数调用方式。
以下使用类的名称 Employee 来实例化,并通过 init 方法接收参数。
1 2 3 4 "创建 Employee 类的第一个对象" emp1 = Employee("Zara" , 2000 )"创建 Employee 类的第二个对象" emp2 = Employee("Manni" , 5000 )
访问属性 可以使用点号 . 来访问对象的属性。使用如下类的名称访问类变量:
1 2 3 emp1.displayEmployee() emp2.displayEmployee()print "Total Employee %d" % Employee.empCount
完整实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Employee : '所有员工的基类' empCount = 0 def __init__ (self, name, salary ): self.name = name self.salary = salary Employee.empCount += 1 def displayCount (self ): print ("Total Employee %d" % Employee.empCount) def displayEmployee (self ): print ("Name : " , self.name, ", Salary: " , self.salary)"创建 Employee 类的第一个对象" emp1 = Employee("Zara" , 2000 )"创建 Employee 类的第二个对象" emp2 = Employee("Manni" , 5000 ) emp1.displayEmployee() emp2.displayEmployee()print ("Total Employee %d" % Employee.empCount)
输出
1 2 3 Name : Zara , Salary: 2000 Name : Manni , Salary: 5000 Total Employee 2
还可以添加,删除,修改类的属性,如下所示:
1 2 3 emp1.age = 7 emp1.age = 8 del emp1.age
也可以使用以下函数的方式来访问属性:
getattr(obj, name[, default]) : 访问对象的属性。
hasattr(obj,name) : 检查是否存在一个属性。
setattr(obj,name,value) : 设置一个属性。如果属性不存在,会创建一个新属性。
delattr(obj, name) : 删除属性。
1 2 3 4 hasattr (emp1, 'age' ) getattr (emp1, 'age' ) setattr (emp1, 'age' , 8 ) delattr (emp1, 'age' )
Python内置类属性
dict : 类的属性(包含一个字典,由类的数据属性组成)
doc :类的文档字符串
name : 类名
module : 类定义所在的模块(类的全名是’main .className’,如果类位于一个导入模块mymod中,那么className.module 等于 mymod)
bases : 类的所有父类构成元素(包含了一个由所有父类组成的元组)
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Employee : '所有员工的基类' empCount = 0 def __init__ (self, name, salary ): self.name = name self.salary = salary Employee.empCount += 1 def displayCount (self ): print ("Total Employee %d" % Employee.empCount) def displayEmployee (self ): print ("Name : " , self.name, ", Salary: " , self.salary)print ("Employee.__doc__:" , Employee.__doc__)print ("Employee.__name__:" , Employee.__name__)print ("Employee.__module__:" , Employee.__module__)print ("Employee.__bases__:" , Employee.__bases__)print ("Employee.__dict__:" , Employee.__dict__)
输出
1 2 3 4 5 Employee.__doc__: 所有员工的基类 Employee.__name__: Employee Employee.__module__: __main__ Employee.__bases__: (<class 'object'>,) Employee.__dict__: {'__module__': '__main__', '__doc__': '所有员工的基类', 'empCount': 0, '__init__': <function Employee.__init__ at 0x000002A3CB538AE0>, 'displayCount': <function Employee.displayCount at 0x000002A3CB539760>, 'displayEmployee': <function Employee.displayEmployee at 0x000002A3CB539800>, '__dict__': <attribute '__dict__' of 'Employee' objects>, '__weakref__': <attribute '__weakref__' of 'Employee' objects>}
Python中的序列化与反序列化 JSON序列化与反序列化 JSON 是一个文件格式,也是一个标准化的数据传输方案,通常网站的后端和前端的交流,移动 APP 和云服务器的交流方式都是通过 JSON。
Python的JSON模块 序列化与反序列化的过程分别叫做:encoding 和 decoding 。
*encoding:* 把Python对象转换成JSON字符串
*decoding:* 把JSON字符串转换成python对象
json模块提供了以下两个方法来进行序列化和反序列化操作:
1 2 3 4 5 dumps(obj, skipkeys=False , ensure_ascii=True , check_circular=True , allow_nan=True , cls=None , indent=None , separators=None , default=None , sort_keys=False , **kw) loads(s, encoding=None , cls=None , object_hook=None , parse_float=None , parse_int=None , parse_constant=None , object_pairs_hook=None , **kw)
此外还有额外两个方法允许我们直接将序列化后得到的json数据保存到文件中,以及直接读取文件中的json数据进行反序列化操作:
1 2 3 4 5 dump(obj, fp, skipkeys=False , ensure_ascii=True , check_circular=True , allow_nan=True , cls=None , indent=None , separators=None , default=None , sort_keys=False , **kw) load(fp, cls=None , object_hook=None , parse_float=None , parse_int=None , parse_constant=None , object_pairs_hook=None , **kw)
实例 序列化
1 2 3 4 5 6 7 import json simple_dict = {'a' :'str中国' , 'c' : True , 'e' : 10 , 'b' : 11.1 , 'd' : None , 'f' : [1 , 2 , 3 ], 'g' :(4 , 5 , 6 )}with open ('simple_dict.txt' , 'w' ) as file_to_write: json.dump(simple_dict, file_to_write)
序列化后字符为
1 {"a": "str\u4e2d\u56fd", "c": true, "e": 10, "b": 11.1, "d": null, "f": [1, 2, 3], "g": [4, 5, 6]}
反序列化
1 2 3 4 5 6 7 import jsonwith open ('simple_dict.txt' , 'r' ) as file_to_read: loaded_simple_dict = json.load(file_to_read) print (loaded_simple_dict) print (type (loaded_simple_dict))
输出
1 2 {'a': 'str中国', 'c': True, 'e': 10, 'b': 11.1, 'd': None, 'f': [1, 2, 3], 'g': [4, 5, 6]} <class 'dict'>
PICKLE序列化与反序列化 pickle模块实现了用于对Python对象结构进行 序列化 和 反序列化 的二进制协议,与json模块不同的是pickle模块序列化和反序列化的过程分别叫做 pickling 和 unpickling:
*pickling:* 是将Python对象转换为字节流的过程;
*unpickling:* 是将字节流二进制文件或字节对象转换回Python对象的过程;
pickle模块与json模块对比
JSON是一种文本序列化格式(它输出的是unicode文件,大多数时候会被编码为utf-8),而pickle是一个二进制序列化格式;
JOSN是我们可以读懂的数据格式,而pickle是二进制格式,我们无法读懂;
JSON是与特定的编程语言或系统无关的,且它在Python生态系统之外被广泛使用,而pickle使用的数据格式是特定于Python的;
默认情况下,JSON只能表示Python内建数据类型,对于自定义数据类型需要一些额外的工作来完成;pickle可以直接表示大量的Python数据类型,包括自定数据类型
pickle实际上可以看作一种独立的语言 ,通过对opcode的更改编写可以执行python代码、覆盖变量等操作。直接编写的opcode灵活性比使用pickle序列化生成的代码更高,有的代码不能通过pickle序列化得到(pickle解析能力大于pickle生成能力)。
实例 序列化
1 2 3 4 5 6 import pickle abc_dict = {'a' : 'str中国' , 'c' : True , 'e' : 10 , 'b' : 11.1 , 'd' : None , 'f' : [1 , 2 , 3 ], 'g' : [4 , 5 , 6 ]}with open ('abc.pk' , 'wb' ) as file_to_write: pickle.dump(abc_dict, file_to_write)
反序列化
1 2 3 4 5 6 7 import picklewith open ('abc.pk' , 'rb' ) as file_to_read: abc_dict_pk = pickle.load(file_to_read) print (abc_dict_pk) print (type (abc_dict_pk))
输出
1 2 {'a': 'str中国', 'c': True, 'e': 10, 'b': 11.1, 'd': None, 'f': [1, 2, 3], 'g': [4, 5, 6]} <class 'dict'>
pickle反序列化漏洞 参考pickle反序列化初探 - 先知社区 (aliyun.com)
前置: pickle可序列化的对象:
None、True 和 False
整数、浮点数、复数
str、byte、bytearray
只包含可封存对象的集合,包括 tuple、list、set 和 dict
定义在模块最外层的函数(使用 def 定义,lambda 函数则不可以)
定义在模块最外层的内置函数
定义在模块最外层的类
__dict__
属性值或 __getstate__()
函数的返回值可以被序列化的类
(详见官方文档pickle —— Python 对象序列化 — Python 3.7.13 文档 )
object.__reduce__()
函数
在开发时,可以通过重写类的 object.__reduce__()
函数,使之在被实例化时按照重写的方式进行。具体而言,python要求 object.__reduce__()
返回一个 (callable, ([para1,para2...])[,...])
的元组,每当该类的对象被unpickle时,该callable就会被调用以生成对象(该callable其实是构造函数)。
在pickle的opcode中, R
的作用与 object.__reduce__()
关系密切:选择栈上的第一个对象作为函数、第二个对象作为参数(第二个对象必须为元组),然后调用该函数。其实 R
正好对应 object.__reduce__()
函数, object.__reduce__()
的返回值会作为 R
的作用对象,当包含该函数的对象被pickle序列化时,得到的字符串是包含了 R
的。