zoukankan      html  css  js  c++  java
  • Python易忽略要点记录二

    为天地立心,为生民立命,为往圣继绝学,为万世开太平。

    类的基本注意点

    私有变量

    1.变量名如果以双下划线__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问。

    2.如果外部代码要获取相应的私有变量,可定义get_xxx方法,如果想修改,定义set_xxx方法。

    3.在Python中,变量名类似__xxx__的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,自己定义时不要使用这种命名方法。

    4.看一个例子:

    class Student(object):
    
        def __init__(self, name, score):
            self.__name = name
            self.__score = score
    
        def get_name(self):
            return self.__name
    
        def get_score(self):
            return self.__score
    
        def print_score(self):
            print('%s: %s' % (self.__name, self.__score))
    
    bart = Student('Bart Simpson', 59)
    print(bart.get_name())
    
    # 非规范方法修改私有变量,不会提示出错。
    bart.__name = 'New Name' # 设置__name变量!
    print(bart.__name)
    
    print(bart.get_name())
    
    # 打印出
    # Bart Simpson
    # New Name
    # Bart Simpson
    

    表面上看,外部代码“成功”地设置了__name变量,但实际上这个__name变量和class内部的__name变量不是一个变量!内部的__name变量已经被Python解释器自动改成了_Student__name,而外部代码给bart新增了一个__name变量,通过最后一个打印可以看出。

    5.注意:不建议这么做,因为不同版本的Python解释器可能会把__name改成不同的变量名。

    python单下划线与双下划线

    1._xxx:不能用于from module import *以单下划线开头的表示的是保护变量,只能允许其本身与子类进行访问。
    2.__xxx:双下划线的表示的是私有类型的变量(private),只能是允许这个类本身进行访问了,连子类也不可以
    3.__xxx__:定义的是特列方法,如__init____str__,__call__

    4.python中不存在protected的概念,上面所说的保护变量,应该把其看作私有变量使用,之所以称为保护变量是为了便于与private类型做区分。

    5.一个例子:

    class A(object):
        def __init__(self, name):
            self._name = name
    
        def get_name(self):
            return self._name
    
    # 这个就是格式化后再当做参数传入对应属性
    a = A('%s/%s' % ('dsad','abc'))
    
    print(a.get_name()) # dsad/abc
    # 下面的可以访问,不过根据python的约定,应该将其视作private,
    # 不要在外部使用它们,良好的编程习惯是不要在外部使用它。
    print(a._name) # dsad/abc
    

    继承和多态

    1.继承最大的好处是子类获得了父类的全部功能。

    2.当子类和父类都存在相同的run()方法时,子类的run()会覆盖了父类的run(),在代码运行的时候,总是会调用子类的run()。

    3.在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类,可用isinstance(object, class)查看。

    4.多态:对于一个变量,我们只需要知道它是Animal类型,无需确切地知道它的子类型,就可以放心地调用run()方法,而具体调用的run()方法是作用在Animal、Dog、Cat还是Tortoise对象上,由运行时该对象的确切类型决定,这就是多态真正的威力:调用方只管调用,不管细节,而当我们新增一种Animal的子类时,只要确保run()方法编写正确,不用管原来的代码是如何调用的。这就是著名的“开闭”原则:

    • 对扩展开放:允许新增Animal子类;
    • 对修改封闭:不需要修改依赖Animal类型的run_twice()等函数。

    5.动态语言的鸭子类型特点决定了继承不像静态语言那样是必须的。

    获取对象信息

    1.判断基本数据类型可以直接写int,str等,但如果要判断一个对象是否是函数,可以使用types模块中定义的常量:

    >>> type(123)==type(456)
    True
    >>> type(123)==int
    True
    >>> type('abc')==type('123')
    True
    >>> type('abc')==str
    True
    >>> import types
    >>> def fn():
    ...     pass
    ...
    >>> type(fn)==types.FunctionType
    True
    >>> type(abs)==types.BuiltinFunctionType
    True
    >>> type(lambda x: x)==types.LambdaType
    True
    >>> type((x for x in range(10)))==types.GeneratorType
    True
    

    2.使用isinstance(),且推荐使用它。

    >>> isinstance('a', str)
    True
    >>> isinstance(123, int)
    True
    # 并且还可以判断一个变量是否是某些类型中的一种,比如下面的代码就可以判断是否是list或者tuple:
    >>> isinstance([1, 2, 3], (list, tuple))
    True
    >>> isinstance((1, 2, 3), (list, tuple))
    True
    

    3.使用dir()

    • 要获得一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list,比如,获得一个str对象的所有属性和方法:
    >>> dir('abc')
    ['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__',
    '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__',
    '__getslice__', '__gt__', '__hash__', '__init__', '__le__', '__len__', 
    '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', 
    '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', 
    '__sizeof__', '__str__', '__subclasshook__', '_formatter_field_name_split',
    '_formatter_parser', 'capitalize', 'center', 'count', 'decode', 'encode', 
    'endswith', 'expandtabs', 'find', 'format', 'index', 'isalnum', 'isalpha',
    'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 
    'lower', 'lstrip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 
    'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 
    'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
    
    • 类似__xxx__的属性和方法在Python中都是有特殊用途的,比如__len__方法返回长度。在Python中,如果你调用len()函数试图获取一个对象的长度,实际上,在len()函数内部,它自动去调用该对象的__len__()方法,所以,下面的代码是等价的:
    >>> len('ABC')
    3
    >>> 'ABC'.__len__()
    3
    
    • 剩下的都是普通属性或方法,比如upper()返回小写的字符串:
    >>> 'abc'.upper()
    'ABC'
    
    • 仅仅把属性和方法列出来是不够的,配合getattr()、setattr()以及hasattr(),我们可以直接操作一个对象的状态:
    >>> class MyObject(object):
    ...     def __init__(self):
    ...         self.x = 9
    ...     def power(self):
    ...         return self.x * self.x
    ...
    >>> obj = MyObject()
    >>> hasattr(obj, 'x') # 有属性'x'吗?
    True
    >>> obj.x
    9
    >>> hasattr(obj, 'y') # 有属性'y'吗?
    False
    >>> setattr(obj, 'y', 19) # 设置一个属性'y'
    >>> hasattr(obj, 'y') # 有属性'y'吗?
    True
    >>> getattr(obj, 'y') # 获取属性'y'
    19
    >>> obj.y # 获取属性'y'
    19
    
    • 如果试图获取不存在的属性,会抛出AttributeError的错误,可以传入一个default参数,如果属性不存在,就返回默认值:
    >>> getattr(obj, 'z', 404) # 获取属性'z',如果不存在,返回默认值404
    404
    
    • 通过内置的一系列函数,我们可以对任意一个Python对象进行剖析,拿到其内部的数据。要注意的是,只有在不知道对象信息的时候,我们才会去获取对象信息。如果可以直接写:
    sum = obj.x + obj.y
    # 就不要写成
    sum = getattr(obj, 'x') + getattr(obj, 'y')
    

    实例属性和类属性

    1.由于Python是动态语言,根据类创建的实例可以任意绑定属性。

    2.如果Student类本身需要绑定一个属性,可以直接在class中定义属性,这种属性是类属性,归Student类所有:

    >>> class Student(object):
    ...     name = 'Student'
    ...
    >>> s = Student() # 创建实例s
    >>> print(s.name) # 打印name属性,因为实例并没有name属性,所以会继续查找class的name属性
    Student
    >>> print(Student.name) # 打印类的name属性
    Student
    >>> s.name = 'Michael' # 给实例绑定name属性
    >>> print(s.name) # 由于实例属性优先级比类属性高,因此,它会屏蔽掉类的name属性
    Michael
    >>> print(Student.name) # 但是类属性并未消失,用Student.name仍然可以访问
    Student
    >>> del s.name # 如果删除实例的name属性
    >>> print(s.name) # 再次调用s.name,由于实例的name属性没有找到,类的name属性就显示出来了
    Student
    

    3.从上面的例子可以看出,在编写程序的时候,千万不要对实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性(这里很容易出错,曾在这里困扰半天)

    面向对象高级编程

    使用__slots__

    1.可以给该实例绑定任何属性和方法,这就是动态语言的灵活性。

    class Student(object):
        pass
    # 添加属性
    s = Student()
    s.name = 'Bob' # 动态给实例绑定一个属性
    print(s.name) # Bob
    
    # 添加方法
    def set_age(self, age): # 定义一个函数作为实例方法
        self.age = age
    from types import MethodType
    s.set_age = MethodType(set_age, s) # 给实例绑定一个方法
    s.set_age(25) # 调用实例方法
    print(s.age) # 测试结果 25
    

    2.给一个实例绑定的方法,对另一个实例不起作用,为了给所有实例都绑定方法,可以给class绑定方法(这里没有讲给类绑定属性,因为方法和给实例绑定属性类似,只需把实例名改成相应的类名即可):

     def set_score(self, score):
        self.score = score
    Student.set_score = set_score
    # 但这种几乎不用,需要这样的时候直接在类中写即可。当然,如果如果是动态创建类,可能还是会用这种方法。
    

    3.上面对实例的属性添加十分灵活,如果想限制,为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性

    class Student(object):
        __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
    
    >>> s = Student() # 创建新的实例
    >>> s.name = 'Michael' # 绑定属性'name'
    >>> s.age = 25 # 绑定属性'age'
    >>> s.score = 99 # 绑定属性'score'
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'Student' object has no attribute 'score'
    

    由于'score'没有被放到__slots__中,所以不能绑定score属性,试图绑定score将得到AttributeError的错误。

    使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的:

    >>> class GraduateStudent(Student):
    ...     pass
    ...
    >>> g = GraduateStudent()
    >>> g.score = 9999
    

    除非在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__

    使用@property

    1.对属性值做相应的限制,通过其可使相应的方法看作属性使用。

    把一个getter方法变成属性,只需要加上@property就可以了,此时,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值,于是,就拥有一个可控的属性操作:

    class Student(object):
    
        @property
        def score(self):
            return self._score
    
        @score.setter
        def score(self, value):
            if not isinstance(value, int):
                raise ValueError('score must be an integer!')
            if value < 0 or value > 100:
                raise ValueError('score must between 0 ~ 100!')
            self._score = value
    
    s = Student()
    s.score = 60 # OK,实际转化为s.set_score(60)
    print(s.score) # OK,实际转化为s.get_score()
    # s.score = 999 # 出错
    

    2.若定义只读属性,只定义getter方法,不定义setter方法就是一个只读属性,详情

    3.@property广泛应用在类的定义中,可以让调用者写出简短的代码,同时保证对参数进行必要的检查,这样,程序运行时就减少了出错的可能性。

    多重继承

    Python允许使用多重继承,因此,MixIn就是一种常见的设计。

    枚举

    1.enum 提供了Enum/IntEnum/unique 三个工具,可以通过继承Enum/IntEnum定义枚举类型。
    其中IntEnum限定枚举成员必须为(或可以转化为)整数类型,而unique方法可以作为修饰器限定枚举成员的值不可重复。

    2.实例

    from enum import Enum, IntEnum, unique
    
    # 例子1:
    try:
        @unique
        class WEEKDAY(Enum):
            MON = 1
            TUS = 2
            WEN = 3
            THU = 4
            FRI = 1
    except ValueError as e:
        print(e)  # 出错duplicate values found in <enum 'weekday'="">: FRI -> MON
    print('********************************分割线**************************************')
    
    
    # 例子2
    class Color(Enum):
        R = 0
        G = 1
        B = 2
    
    # 可以赋值给变量,但不可不可实例化,如Color.R = 3就是错的
    red = Color(0)
    green = Color(1)
    blue = Color(2)
    
    print(red, green, blue)
    
    # 可以进行比较判断
    print(red is Color.R)
    print(red == Color.R)
    print(red is blue)
    print(green != Color.B)
    print(red == 0)  # 不等于任何非本枚举类的值
    
    # 由于枚举成员本身也是枚举类型,因此也可以通过枚举成员找到其它成员
    print(red.B)
    print(red.B.G.R)
    
    # 通过属性访问
    print(red.name, ':', red.value)
    
    # 遍历访问
    for color in Color:
        print(color.name, '=>', color.value)
    
    # 如果要遍历Enum中带有重复的枚举成员,可能会用到__members__方法,注意,这里没有重复的
    for name, member in Color.__members__.items():
        print(name, '=>', member, ',', member.value)
    
    

    3.可参考:Python 中的枚举类型

    元类(metaclass)

    动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的。

    1.这个概念比较深,而且不太好理解,个人感觉未能很好地理解,下面是看到的很好的文章。

    2.运用实例:元类的主要用途是创建API,一个典型的例子是Django ORM,下面是源码链接。

    3.需要注意的是,Python2和Python3中的用法形式不同,这点可以认真看看原文和上面的源代码例子。


    错误、调试和测试

    错误处理

    1.Python使用try...except...finally...进行错误处理。需要注意的是如果在except后又else语句,若没有错误则会执行else的语句,反之则不执行。

    2.记录错误,Python内置的logging模块可以非常容易地记录错误信息。通过配置,logging可以把错误记录到日志文件里,方便事后排查。

    3.抛出错误,捕获错误目的只是记录一下,便于后续追踪。但是,由于当前函数不知道应该怎么处理该错误,所以,最恰当的方式是继续往上抛,让顶层调用者去处理。Python中使用raise语把当前错误抛出。

    调试

    1.直接用print()查看结果调试,麻烦且效率不高。

    2.使用断言(assert)如果断言失败,assert语句本身就会抛出AssertionError

    # 使用断言
    def foo(s):
        n = int(s)
        # assert的意思是,表达式n != 0应该是True,否则,抛出AssertionError
        assert n != 0, 'n is zero!'
        return 10 / n
    
    foo('0') #运行结果 AssertionError: n is zero!
    

    3.把print()替换为logging是第3种方式,和assert比,logging不会抛出错误,而且可以输出到文件。

    4.第4种方法是使用pdb,启动Python的调试器pdb,让程序以单步方式运行,可以随时查看运行状态。

    • 命令行模式下运行某py文件python -m pdb xxx.py
    • 以参数-m pdb启动后,pdb定位到下一步要执行的代码,有->指示,可输入命令l来查看源码
    • 输入命令n可以单步执行代码
    • 任何时候都可以输入命令p 变量名来查看变量值,如查看变量s,则为p s
    • 输入命令q结束调试,退出程序。

    5.方法太笨重了些,可以改进:使用pdb.set_trace()

    • 这个方法也是用pdb,但是不需要单步执行,只需要import pdb,然后,在可能出错的地方放一个pdb.set_trace(),就可以设置一个断点。
    • 运行代码,程序会自动在pdb.set_trace()暂停并进入pdb调试环境,可以用命令p查看变量,或者用命令c继续运行

    6.使用IDE,效率较高,如Visual Studio Code,pycharm等。

    单元测试

    1.单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。

    2.为了编写单元测试,需要引入Python自带的unittest模块,编写单元测试时,需要编写一个测试类,从unittest.TestCase继承。

    3.以test开头的方法就是测试方法,不以test开头的方法不被认为是测试方法,测试的时候不会被执行,对每一类测试都需要编写一个test_xxx()方法。

    4.unittest.TestCase提供了很多内置的条件判断,只需要调用这些方法就可以断言输出是否是所期望的。最常用的断言就是assertEqual()

    self.assertEqual(abs(-1), 1) # 断言函数返回的结果与1相等
    

    另一种重要的断言就是期待抛出指定类型的Error,如KeyError,AttributeError等。

    5.运行

    • 方法一:加上两行代码,然后正常运行。
    if __name__ == '__main__':
        unittest.main()
    
    • 方法二:在命令行通过参数-m unittest直接运行单元测试,如python -m unittest py文件名

    6.可以在单元测试中编写两个特殊的setUp()tearDown()方法。这两个方法会分别在每调用一个测试方法的前后分别被执行。

    文档测试

    1.Python内置的“文档测试”doctest模块可以直接提取注释中的代码并执行测试。

    2.doctest严格按照Python交互式命令行的输入和输出来判断测试结果是否正确。只有测试异常的时候,可以用...表示中间一大段烦人的输出。

    3.doctest非常有用,不但可以用来测试,还可以直接作为示例代码。通过某些文档生成工具,就可以自动把包含doctest的注释提取出来。用户看文档的时候,同时也看到了doctest

    4.一个例子,对函数fact(n)编写doctest并执行

    def fact(n):
        '''
        Calculate 1*2*...*n
    
        >>> fact(1)
        1
        >>> fact(10) # 不用自己算,运行时出错会有结果,到时再改即可
        3628800
        >>> fact(-1) # doctest严格按照Python交互式命令行的输入和输出来判断测试结果是否正确。只有测试异常的时候,可以用...表示中间一大段烦人的输出。
        Traceback (most recent call last):
          ...
        ValueError
        '''
        if n < 1:
            raise ValueError()
        if n == 1:
            return 1
        return n * fact(n - 1)
    
    if __name__ == '__main__':
        import doctest
        doctest.testmod()
    

    5.什么输出也没有。这说明编写的doctest运行都是正确的。如果程序有问题,比如fact(10)下的3628800去掉,再运行就会报错。

    IO编程

    1.IO编程中,Stream(流)是一个很重要的概念,可以把流想象成一个水管,数据就是水管里的水,但是只能单向流动。

    • Input Stream就是数据从外面(磁盘、网络)流进内存
    • Output Stream就是数据从内存流到外面去。

    对于浏览网页来说,浏览器和服务器之间至少需要建立两根水管,才可以既能发数据,又能收数据。

    2.由于CPU和内存的速度远远高于外设的速度,所以,在IO编程中,就存在速度严重不匹配的问题,于是出现了两种模式:

    • 同步IO
    • 异步IO

    3.操作IO的能力都是由操作系统提供的,每一种编程语言都会把操作系统提供的低级C接口封装起来方便使用,Python也不例外。

    文件读写

    1.Python中有open(),write(),read()等函数用于文件读写,但用完后得
    close()

    2.可以直接使用with open() as f自动调用close()方法简洁高效。

    3.关于读文件内容常用的几个方法:

    • read()会一次性读取文件的全部内容,如果文件太大,会出错,所以,要保险起见,可以反复调用read(size)方法,每次最多读取size个字节的内容。
    • readline()可以每次读取一行内容
    • readlines()一次读取所有内容并按行返回list

    4.file-like Object

    open()函数返回的这种有个read()方法的对象,在Python中统称为file-like Object。除了file外,还可以是内存的字节流,网络流,自定义流等等。file-like Object不要求从特定类继承,只要写个read()方法就行。StringIO就是在内存中创建的file-like Object,常用作临时缓冲。

    5.一些例子

    # 写
    # f = open("file.txt", 'a', encoding='utf-8', errors='ignore')
    # f.write("I try to write something new to file\n")
    # f.close()
    
    # 推荐用法
    # with open("file.txt", 'a', encoding='utf-8', errors='ignore') as f:
    #     for i in range(6):
    #         f.write("I try to write something new to file\n")
    
    
    # 读
    f = open("file.txt", 'r', encoding='utf-8', errors='ignore')  # 默认以只读的形式打开
    print(f.readline())  # 因为\n是写入的,所以打印出来时加上每一行的回车,即两个回车
    print("当前指针位置", f.tell()) # tell 方法获取当前文件读取指针的位置
    print(f.readline().strip()) # 可以用strip()将行末尾的换行符除去
    print("当前指针位置", f.tell())
    print(f.read())
    print("读取当前指针位置", f.tell())
    # seek 方法,用于移动文件读写指针到指定位置,有两个参数,第一个offset: 偏移量,需要向前或向后的字节数,正为向后,负为向前;
    # 第二个whence: 可选值,默认为0,表示文件开头,1表示相对于当前的位置,2表示文件末尾
    # 注意,关于偏移量,只有在以'b'(二进制)的条件下才可用,不然只可以为0。
    f.seek(0, 0)
    print("读取当前指针位置", f.tell())
    f.seek(0, 2)
    print("读取当前指针位置", f.tell())
    f.seek(0)
    print("读取当前指针位置", f.tell())
    print(f.closed)
    print(f.mode)
    f.close()
    print(f.closed)
    # print(f.truncate(10))
    
    print('**********************************分割线***********************************')
    
    # 推荐用法
    with open('file.txt', mode='r', encoding='utf-8', errors='ignore') as f:
        f = f.readlines()
        print(f)
    
        for each in f:
            print(each, end='  ') # two space
    

    6.所有模式的定义及含义可以参考Python的官方文档

    StringIO和BytesIO

    1.StringIO顾名思义就是在内存中读写str。

    • 要把str写入StringIO,需要先创建一个StringIO,然后,像文件一样写入即可:
    from io import StringIO
    
    f = StringIO()
    f.write('Hello World')
    s = f.readline()
    print(s) # 这里读不出来,是因为文件读写指针已经到文件末尾了,可用getvalue()来读
    print(f.getvalue())
    # 或者先设置文件指针位置
    f.seek(0)
    s = f.readline()
    print(s)
    
    • 也可以用一个str初始化StringIO,然后,像读文件一样读取
    f = StringIO('abc')
    print(f.read())
    

    2.StringIO操作的只能是str,如果要操作二进制数据,就需要使用BytesIO。

    • BytesIO实现了在内存中读写bytes,创建一个BytesIO,然后写入一些bytes:
    f = BytesIO()
    f.write('中文'.encode('utf-8'))
    print(f.getvalue())
    
    • 和StringIO类似,可以用一个bytes初始化BytesIO,然后,像读文件一样读取
    f = BytesIO(b'abc')
    print(f.read())
    

    3.StringIO和BytesIO是在内存中操作str和bytes的方法,使得和读写文件具有一致的接口。

    操作文件和目录

    1.Python的os模块封装了操作系统的目录和文件操作,注意这些函数有的在os模块中,有的在os.path模块中。

    2.记的话,有些难,用时看文档:操作系统接口模块

    3.几个小例子:

    # 列出当前目录下的所有目录:
    [x for x in os.listdir('.') if os.path.isdir(x)]
    # 列出当前目录下所有的.py文件:
    [x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1]=='.py']
    

    序列化

    1.把变量从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling,序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling。

    2.Python提供了pickle模块来实现序列化,相关操作如下:

    import pickle
    
    d = dict(name='Bob', age=20, score=88)
    # pickle.dumps()方法把任意对象序列化成一个bytes,然后,就可以把这个bytes写入文件。
    print('序列化后的bytes为:', pickle.dumps(d))
    
    # 或者使用pickle.dump()直接把对象序列化后写入一个file-like Object
    f = open('dump.txt', 'wb')
    pickle.dump(d, f)
    f.close()
    
    # 把对象从磁盘读到内存时,可以先把内容读到一个bytes,然后用pickle.loads()方法反序列化出对象。
    # 也可以直接用pickle.load()方法从一个file-like Object中直接反序列化出对象。
    
    f = open('dump.txt', 'rb')
    d = pickle.load(f)
    f.close()
    print('反序列化后的内容为:', d)
    
    # 输出为:
    # 序列化后的bytes为: b'\x80\x03}q\x00(X\x04\x00\x00\x00nameq\x01X\x03\x00\x00\x00Bobq\x02X\x03\x00\x00\x00ageq\x03K\x14X\x05\x00\x00\x00scoreq\x04KXu.'
    # 反序列化后的内容为: {'name': 'Bob', 'age': 20, 'score': 88}
    

    3.JSON

    • Pickle的问题和所有其他编程语言特有的序列化问题一样,就是它只能用于Python,而JSON对象序列化是标准格式,可以被所有语言读取,也可以方便地存储到磁盘或者通过网络传输。
    • Python内置的json模块提供了非常完善的Python对象到JSON格式的转换。
    • 使用方法:和pickle使用方法类似,如下:
    import json
    
    d = dict(name='Bob', age=20, score=88)
    # dumps()方法返回一个str,内容就是标准的JSON。
    print('序列化后的bytes为:', json.dumps(d))
    
    # 类似的,dump()方法可以直接把JSON写入一个file-like Object。
    # 注意,这里是以'w'的形式写,因为dump()方法返回的是str
    f = open('dumps.txt', 'w')
    json.dump(d, f)
    f.close()
    
    # 要把JSON反序列化为Python对象,用loads()或者对应的load()方法,
    # 前者把JSON的字符串反序列化,后者从file-like Object中读取字符串并反序列化:
    f = open('dumps.txt', 'r')
    d = json.load(f)
    f.close()
    print('反序列化后的内容为:', d)
    
    # 输出为:
    # 序列化后的bytes为: {"name": "Bob", "age": 20, "score": 88}
    # 反序列化后的内容为: {'name': 'Bob', 'age': 20, 'score': 88}
    
    • 序列化对象:
    import json
    
    class Student(object):
        def __init__(self, name, age, score):
            self.name = name
            self.age = age
            self.score = score
    
    s = Student('Bob', 20, 88)
    
    # print(json.dumps(s))
    # 可选参数default就是把任意一个对象变成一个可序列为JSON的对象,
    # 只需要为Student专门写一个转换函数,再把函数传进去即可:
    def student2dict(std):
        return {
            'name': std.name,
            'age': std.age,
            'score': std.score
        }
    
    # 如果要把JSON反序列化为一个Student对象实例,loads()方法首先转换出一个dict对象,
    # 然后,我们传入的object_hook函数负责把dict转换为Student实例:
    def dict2student(d):
        return Student(d['name'], d['age'], d['score'])
    
    # 这样即可序列化对象
    print(json.dumps(s, default=student2dict))
    
    # 或者直接这样,因为通常class的实例都有一个__dict__属性,它就是一个dict,用来存储实例变量。
    # 也有少数例外,比如定义了__slots__的class。
    print(json.dumps(s, default=lambda obj: obj.__dict__))
    print(s.__dict__)
    
    # 在反序列化时必须要有相应的方法
    json_str = json.dumps(s, default=student2dict)
    json_str1 = json.dumps(s, default=lambda obj: obj.__dict__)
    print(json.loads(json_str, object_hook=dict2student))
    print(json.loads(json_str1, object_hook=dict2student))
    
    # 输出为:
    # {"name": "Bob", "age": 20, "score": 88}
    # {"name": "Bob", "age": 20, "score": 88}
    # {'name': 'Bob', 'age': 20, 'score': 88}
    # <__main__.Student object at 0x000001B58BC54630>
    # <__main__.Student object at 0x000001B58BC546D8>
    
  • 相关阅读:
    FFOM_秒交易行
    FFOM_脚本源代码
    农药_挂周金币
    保存数据,父页面列表数据更新
    点击按钮不弹出新窗口
    GridView1_RowDeleting 弹出确认对话框
    判断复选框
    获取Guid
    2019 gplt团体程序设计天梯赛总结
    Codeforces Round #550 (Div. 3)E. Median String
  • 原文地址:https://www.cnblogs.com/clwsec/p/15679759.html
Copyright © 2011-2022 走看看