步骤1:创建实例
在Python中,模块名使用小写字母开头(如person.py),而类名使用一个大写字母开头(如 class Person),这是通用的惯例。
# File person.py(start)
class Person:
编写构造函数(赋值并初始化实例属性)
实例对象一般存在记录对象的属性,这些属性通常通过给类方法函数中的self属性赋值来创建。赋给实例属性第一个值的通常方法是,在__init__构造函数方法中将它们赋给self,构造函数方法包含了每次创建一个实例的时候Python会自动运行的代码。
class Person:
def __init__(self,name,job,pay):
self.name = name
self.job = job
self.pay = pay
在进行中测试
bob = Person('Bob Smith')
sue = Person('Sue Jones', job = 'dev', pay = 100000)
print(bob.name, bob.pay)
print(sue.name, sue.pay)
运行结果:
Bob Smith 0
Sue Jones 100000
以两种方式使用代码
把person.py作为顶层文件或者导入文件运行时,我们希望某些操作只在作为运行的顶层文件时才显示,可以使用__main__操作,如下所示:
if __name__ == '__main__':
bob = Person('Bob Smith')
sue = Person('Sue Jones', job = 'dev', pay = 100000)
print(bob.name, bob.pay)
print(sue.name, sue.pay)
这样,当程序以当前顶层文件运行时显示相关信息,而作为模块导入时不运行此类信息
步骤2:添加行为方法
编写方法
我们利用封装的概念添加行为方法,封装的思想就是把操作逻辑包装到界面之后,这样,每种操作在我们的程序里只编码一次。通过这种方式,如果将来需要修改,只需要修改一个版本。
class Person:
def __init__(self,name,job = 'None',pay = 0):
self.name = name
self.job = job
self.pay = pay
def lastName(self):
return self.name.split()[-1]
def giveRaise(self, percent):
self.pay = int(self.pay*(1 + percent))
if __name__ == '__main__':
bob = Person('Bob Smith')
sue = Person('Sue Jones', job = 'dev', pay = 100000)
print(bob.name, bob.pay)
print(sue.name, sue.pay)
print(bob.lastName(),sue.lastName())
sue.giveRaise(.10)
print(sue.pay)
运行结果:
Bob Smith 0
Sue Jones 100000
Smith Jones
110000
步骤3:运算符重载
运算符重载:在一个类中编写这样的方法,当方法在类的实例上运行的时候,方法截获并处理内置的操作。
提供打印显示(__str__)
由于打印运行__str__并且交互提示使用__rerp__回应结果。
def __str__(self):
return '[Person: %s, %s]' % (self.name, self.pay)
调用:
if __name__ == '__main__':
bob = Person('Bob Smith')
sue = Person('Sue Jones', job = 'dev', pay = 100000)
print(bob)
print(sue)
print(bob.lastName(),sue.lastName())
sue.giveRaise(.10)
print(sue)
运行结果:
[Person: Bob Smith, 0]
[Person: Sue Jones, 100000]
Smith Jones
[Person: Sue Jones, 110000]
步骤4:通过子类定制行为
编写子类
class Manage(Person):
def giveRaise(self, percent, bonus = .10):
Person.giveRaise(self,percent + bonus)
调用:
tom = Manage('Tom Jones', job = 'dev', pay = 100000)
tom.giveRaise(.10)
print(tom.lastName())
print(tom)
运行结果:
Jones
[Person: Tom Jones, 120000]
测试代码的末尾按照Person的__str__中定义的漂亮格式来显示:Manager对象获取lastName,而__init__构造函数方法的代码通过继承“免费”从Person得到。
多态的作用
多态:根据对象的类型来分派具体使用哪种方法
测试如下:
print('--All three--')
for object in (bob,sue,tom):
object.giveRaise(.10)
print(object)
运行结果:
--All three--
[Person: Bob Smith, 0]
[Person: Sue Jones, 121000]
[Person: Tom Jones, 144000]
扩展
我们可以为Manage添加独特的、Person中没有的方法。
步骤5:定制构造函数
class Manage(Person):
def __init__(self, name, pay):
Person.__init__(self, name, 'mgr', pay)
def giveRaise(self, percent, bonus = .10):
Person.giveRaise(self,percent + bonus)
通过类名直接调用并显示地传递self实例,从而运行超类的版本。在构造的时候,Python自身使用继承来查找并调用唯一的一个__init__方法,也就是类树中最低的一个。如果你需要在构造的时候运行更高的__init__方法(并且常常会这么做),必须通过超类的名称手动调用它们。
步骤6:使用内省工具
特殊类属性
Python的内省工具,它们是特殊的属性和函数,允许我们访问对象实现的一些内部机制。
内置的instance.__class__属性提供了一个从实例到创建它的类的链接。类反过来有一个__name__(就像模块一样),还有一个__base__序列,提供了超类的访问。我们使用这些来打印创建一个实例的类的名字,而不是通过硬编码来做到。
内置的object.__dict__属性提供了一个字典,带有一个键/值对,以便每个属性都附加到一个命名控件对象(包括模块、类和实例)。由于它是字典,因此我们可以获取键的列表、按照键来索引、迭代其键,等等,从而广泛地处理所有的属性。我们使用这些来打印出任何实例的每个属性,而不是在定制显示中硬编码。
from person import Person
bob = Person('Bob Smith')
print(bob)
print(bob.__class__)
print(bob.__class__.__name__)
print(list(bob.__dict__.keys()))
for key in bob.__dict__:
print(key, '=>', getattr(bob, key))
运行结果:
[Person: Bob Smith, 0]
<class 'person.Person'>
Person
['name', 'job', 'pay']
name => Bob Smith
job => None
pay => 0
一种通用显示工具
class AttrDisplay:
def gatherAttrs(self):
attrs = []
for key in sorted(self.__dict__):
attrs.append('%s=%s' % (key, getattr(self, key)))
return ', '.join(attrs)
def __str__(self):
return '[%s: %s]' % (self.__class__.__name__,self.gatherAttrs())
if __name__ == '__main__':
class TopTest(AttrDisplay):
count = 0
def __init__(self):
self.attr1 = TopTest.count
self.attr2 = TopTest.count + 1
TopTest.count += 2
class SubTest(TopTest):
pass
X, Y= TopTest(), SubTest()
print(X)
print(Y)
类的最终形式
要在类中使用这一通用工具,所需要做的只是从其模块导入它,使用继承将其混合到顶层类中,并且删除掉我们之前编写的专门的__str__方法。
from classtools import AttrDisplay
class Person(AttrDisplay):
def __init__(self,name,job = 'None',pay = 0):
self.name = name
self.job = job
self.pay = pay
def lastName(self):
return self.name.split()[-1]
def giveRaise(self, percent):
self.pay = int(self.pay*(1 + percent))
class Manage(Person):
def __init__(self, name, pay):
Person.__init__(self, name, 'mgr', pay)
def giveRaise(self, percent, bonus = .10):
Person.giveRaise(self,percent + bonus)
if __name__ == '__main__':
bob = Person('Bob Smith')
sue = Person('Sue Jones', job = 'dev', pay = 100000)
print(bob)
print(sue)
print(bob.lastName(),sue.lastName())
sue.giveRaise(.10)
print(sue)
tom = Manage('Tom Jones',pay = 100000)
tom.giveRaise(.10)
print(tom.lastName())
print(tom)
运行结果:
步骤7:把对象存储到数据库中
Pickle和Shelve
Pickle
任意Python对象和字节串之间的序列化
dbm(在Python2.6中叫做anydbm)
实现一个可通过键访问的文件系统,以存储字符串
shelve
使用另两个模块按照键把Python对象存储到一个文件中
pickle模块是一种非常通用的对象格式化和解格式化工具:对于内存中几乎任何的Python对象,它都能聪明地把对象转换为字符串,这个字符串可以随后用来在内存中重新构建最初的对象。
一个shelve的pickle化的对象看上去就像是字典一一我们通过键索引来访问、指定键来存储,并且使用len、in和dict.keys这样的字典工具来获取信息。Shelve自动地把字典操作映射到存储在文件中的对象。
对于脚本来说,一个shelve和一个常规的字典之间的唯一编码区别:一开始必须打开shelve并且在修改之后必须关闭它。实际的效果是,一个shelve提供了一个简单的数据库来按照键存储和获取本地的Python对象,并由此使他们跨程序运行而保持持久化。
在shelve数据库中存储对象:
from person import Person,Manage
bob = Person('Bob Smith')
sue = Person('Sue Jones', job = 'dev', pay = 100000)
tom = Manage('Tom Jones',50000)
import shelve
db = shelve.open('persondb')
for object in (bob, sue, tom):
db[object.name] = object
db.close()
运行makedb.py文件后,我们在工程目录下看到产生了如下三个文件:
交互地探索shelve
import shelve
db = shelve.open('persondb')
print(len(db))
print(db.keys())
bob = db['Bob Smith']
print(bob)
print(bob.lastName())
for key in db:
print(key, '=>', db[key])
for key in sorted(db):
print(key, '=>', db[key])
运行结果: