Python类 - littleboy12580/learning_python GitHub Wiki

Python的类

运算符重载

在类中通过双下划线命名的方法(X)是特殊钩子,可以通过这种命名来对运算符进行重载

class ThirdClass(SecondClass):    #继承了SecondClass
    def __init__(self,value):        #构造函数
        self.data = value
    def __add__(self,other):       #重载了+运算符
        return ThirdClass(self.data + other)
    def __str__(self):
        return '[ThirdClass: %s]' % self.data      #重载了字符串显示

a = ThirdClass('abc')
print(a)
b = a + 'xyz'
print(b)

上面代码最后的结果为

[ThirdClass: abc]
[ThirdClass: abcxyz]

类的方法

类中的方法必须明确列出self参数,否则Python无法猜测简单函数是否最终会变成类的方法。

特殊类属性

Python中有两个常用的内省工具可以让我们访问对象实现的一些内部机制

instance.class

该属性提供了一个从实例到创建它的类的链接,有一个__name__和一个__bases__序列,name显示实例对应的类的名称,bases显示对应类的基类

bob = Person()
bob.__class__
bob.__class__.__name__

第一个返回的是类对象,第二个返回的是类对象的名称

object.dict

该属性提供一个字典,能够获取对象中的各种属性

list(bob.__dict__.keys)

返回的是bob实例里的各种属性名

对象持久化

Python通过3个标准的库模块来实现对象持久化

pickle

任意Python对象和字节串之间的序列化:使用pickle把对象存储为简单的普通文件,载入的时候使用unpickle来还原最初的对象

dbm

实现一个可通过键访问的文件系统,以存储字符串

shelve

使用另两个模块按照键把Python对象存储到一个文件中:shelve使用pickle把一个对象转换为其pickle化的字符串,并将其存储在一个dbm文件中的键之下;随后载入的时候shelve通过键获取pickle化的字符串,并用pickle在内存中重新创建最初的对象

bob = Person()
sue = Person()
tom = Person()
import shelve
db = shelve.open('filename')        #创建dbm文件系统
for object in (bob,sue,tom):
    db[object.name] = object          #将实例名称作为键值
db.close()

json

一种轻量级数据交换格式,相对于XML而言更简单,也易于阅读和编写,机器也方便解析和生成,Json是JavaScript中的一个子集。

Python2.6开始加入了JSON模块,无需另外下载,Python的Json模块序列化与反序列化的过程分别是 encoding和 decoding

encoding:把一个Python对象编码转换成Json字符串

decoding:把Json格式字符串解码转换成Python对象

对于简单数据类型(string、unicode、int、float、list、tuple、dict),可以直接处理。

import json
 
obj = [[1,2,3],123,123.123,'abc',{'key1':(1,2,3),'key2':(4,5,6)}]
encodedjson = json.dumps(obj)
print repr(obj)
print encodedjson

运行结果为

[[1, 2, 3], 123, 123.123, 'abc', {'key2': (4, 5, 6), 'key1': (1, 2, 3)}] 
[[1, 2, 3], 123, 123.123, "abc", {"key2": [4, 5, 6], "key1": [1, 2, 3]}]

在json的编码过程中,会存在从python原始类型向json类型的转化过程,具体的转化对照如下: revertjson

对于encode,我们使用dump()方法,对于decode,我们使用loads()方法

decodejson = json.loads(encodedjson)
print type(decodejson)
print decodejson[4]['key1']
print decodejson

输出为:

<type 'list'> 
[1, 2, 3]
[[1, 2, 3], 123, 123.123, u'abc', {u'key2': [4, 5, 6], u'key1': [1, 2, 3]}]

loads方法返回了原始的对象,但是仍然发生了一些数据类型的转化。比如,上例中‘abc’转化为了unicode类型。从json到python的类型转化对照如下: jsonrevert

json.dumps方法提供了很多好用的参数可供选择,比较常用的有sort_keys(对dict对象进行排序,我们知道默认dict是无序存放的),separators,indent等参数。

排序功能使得存储的数据更加有利于观察,也使得对json输出的对象进行比较,例如:

data1 = {'b':789,'c':456,'a':123}
data2 = {'a':123,'b':789,'c':456}
d1 = json.dumps(data1,sort_keys=True)
d2 = json.dumps(data2)
d3 = json.dumps(data2,sort_keys=True)
print d1
print d2
print d3
print d1==d2
print d1==d3

输出:
{"a": 123, "b": 789, "c": 456} 
{"a": 123, "c": 456, "b": 789} 
{"a": 123, "b": 789, "c": 456} 
False 
True

indent参数是缩进的意思,它可以使得数据存储的格式变得更加优雅

data1 = {'b':789,'c':456,'a':123}
d1 = json.dumps(data1,sort_keys=True,indent=4)
print d1

输出:
{ 
    "a": 123, 
    "b": 789, 
    "c": 456 
}

输出的数据被格式化之后,变得可读性更强,但是却是通过增加一些冗余的空白格来进行填充的。json主要是作为一种数据通信的格式存在的,而网络通信是很在乎数据的大小的,无用的空格会占据很多通信带宽,所以适当时候也要对数据进行压缩。separator参数可以起到这样的作用,该参数传递是一个元组,包含分割对象的字符串

print 'DATA:', repr(data)
print 'repr(data)             :', len(repr(data))
print 'dumps(data)            :', len(json.dumps(data))
print 'dumps(data, indent=2)  :', len(json.dumps(data, indent=4))
print 'dumps(data, separators):', len(json.dumps(data, separators=(',',':')))

# 输出:
DATA: {'a': 123, 'c': 456, 'b': 789} 
repr(data)             : 30 
dumps(data)            : 30 
dumps(data, indent=2)  : 46 
dumps(data, separators): 25

处理自己的数据

json模块不仅可以处理普通的python内置类型,也可以处理我们自定义的数据类型,而往往处理自定义的对象是很常用的。

首先,我们定义一个类Person

class Person(object):
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def __repr__(self):
        return 'Person Object name : %s , age : %d' % (self.name,self.age)
if __name__  == '__main__':
    p = Person('Peter',22)
    print p

如果直接通过json.dumps方法对Person的实例进行处理的话,会报错,因为json无法支持这样的自动转化。通过上面所提到的json和python的类型转化对照表,可以发现,object类型是和dict相关联的,所以我们需要把我们自定义的类型转化为dict,然后再进行处理。这里,有两种方法可以使用

第一种方法:自己写转换函数

import Person
import json
 
p = Person.Person('Peter',22)
 
def object2dict(obj):
    #convert object to a dict
    d = {}
    d['__class__'] = obj.__class__.__name__
    d['__module__'] = obj.__module__
    d.update(obj.__dict__)
    return d
 
def dict2object(d):
    #convert dict to object
    if'__class__' in d:
        class_name = d.pop('__class__')
        module_name = d.pop('__module__')
        module = __import__(module_name)
        class_ = getattr(module,class_name)
        args = dict((key.encode('ascii'), value) for key, value in d.items()) #get args
        inst = class_(**args) #create new instance
    else:
        inst = d
    return inst
 
d = object2dict(p)
print d
#{'age': 22, '__module__': 'Person', '__class__': 'Person', 'name': 'Peter'}
 
o = dict2object(d)
print type(o),o
#<class 'Person.Person'> Person Object name : Peter , age : 22
 
dump = json.dumps(p,default=object2dict)
print dump
#{"age": 22, "__module__": "Person", "__class__": "Person", "name": "Peter"}
 
load = json.loads(dump,object_hook = dict2object)
print load
#Person Object name : Peter , age : 22

第二种:继承JSONEncoder和JSONDecoder类,覆写相关方法

JSONEncoder类负责编码,主要是通过其default函数进行转化,我们可以override该方法。同理对于JSONDecoder。

import Person
import json
 
p = Person.Person('Peter',22)
 
class MyEncoder(json.JSONEncoder):
    def default(self,obj):
        #convert object to a dict
        d = {}
        d['__class__'] = obj.__class__.__name__
        d['__module__'] = obj.__module__
        d.update(obj.__dict__)
        return d
 
class MyDecoder(json.JSONDecoder):
    def __init__(self):
        json.JSONDecoder.__init__(self,object_hook=self.dict2object)
    def dict2object(self,d):
        #convert dict to object
        if'__class__' in d:
            class_name = d.pop('__class__')
            module_name = d.pop('__module__')
            module = __import__(module_name)
            class_ = getattr(module,class_name)
            args = dict((key.encode('ascii'), value) for key, value in d.items()) #get args
            inst = class_(**args) #create new instance
        else:
            inst = d
        return inst
 
 
d = MyEncoder().encode(p)
o =  MyDecoder().decode(d)
 
print d
print type(o), o

命名空间

变量名解析规则

  • 无点号运算的变量名(eg:X)与作用域相对应
  • 点号的属性名(eg:object.X)使用的是对象的命名空间
  • 有些作用域会对对象的命名空间进行初始化(模块和类)

命名空间字典

  • 模块的命名空间实际上是以字典的形式实现,类与实例对象也一样。
  • 属性点号运算其实内部就是字典的索引运算,而属性继承其实就是搜索链接的字典
  • 实例通过__class__属性链接到它对应的类,类则通过__bases__属性获得其父类的元组
  • 当类为self属性赋值时,会填入实例对象。即属性最后会位于实例的属性命名空间字典内,而不是类的。
  • 每个实例都有独立的命名空间字典

类与模块的关系

模块

  • 是数据/逻辑包
  • 通过编写Python文件或C扩展来创建
  • 通过导入来使用

  • 实现新的对象
  • 由class语句创建
  • 通过调用来使用
  • 总是位于一个模块中
  • 支持一些模块所不支持的额外功能,如运算符重载,多实例生成和继承