import xml.etree.ElementTree as etree
import json
# JSONConnector类解析JSON文件,通过parsed_data()方法以一个字典(dict)的形式 返回数据。
# 修饰器property使parsed_data()显得更像一个常规的变量,而不是一个方法
class JSONConnector:
def __init__(self, filepath):
self.data = dict()
with open(filepath, mode='r', encoding='utf-8') as f:
self.data = json.load(f)
@property
def parsed_data(self):
return self.data
# XMLConnector类解析xml数据
class XMLConnector:
def __init__(self, filepath):
self.tree = etree.parse(filepath)
@property
def parsed_data(self):
return self.tree
# 函数connection_factory是一个工厂方法,
# 基于输入文件路径的扩展名返回一个JSONConnector或XMLConnector的实例
def connection_factory(filepath):
if filepath.endswith('json'):
connector = JSONConnector
elif filepath.endswith('xml'):
connector = XMLConnector
else:
raise ValueError('Cannot connect to {}'.format(filepath))
return connector(filepath)
# 函数connect_to()对connection_factory()进行包装,添加了异常处理
def connect_to(filepath):
factory = None
try:
factory = connection_factory(filepath)
except ValueError as ve:
print(ve)
return factory
# 注意,虽然JSONConnector和XMLConnector拥有相同的接口,
# 但是对于parsed_data() 返回的数据并不是以统一的方式进行处理。
# 对于每个连接器,需使用不同的Python代码来处理。
def main():
sqlite_factory = connect_to('data/person.sq3')
print()
xml_factory = connect_to('data/person.xml')
xml_data = xml_factory.parsed_data
liars = xml_data.findall(".//{}[{}='{}']".format('person',
'lastName', 'Liar'))
print('found: {} persons'.format(len(liars)))
for liar in liars:
print('first name: {}'.format(liar.find('firstName').text))
print('last name: {}'.format(liar.find('lastName').text))
[print('phone number ({})'.format(p.attrib['type']),
p.text) for p in liar.find('phoneNumbers')]
print()
json_factory = connect_to('data/donut.json')
json_data = json_factory.parsed_data
print('found: {} donuts'.format(len(json_data)))
for donut in json_data:
print('name: {}'.format(donut['name']))
print('price: ${}'.format(donut['ppu']))
[print('topping: {} {}'.format(t['id'], t['type'])) for t in donut['topping']]
if __name__ == '__main__':
main()
# Frog青蛙碰到Bug虫子,就吃掉它
class Frog:
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
# 这里模拟Frog和Bug类之间的交互
def interact_with(self, obstacle):
print('{} the Frog encounters {} and {}!'.format(self,
obstacle, obstacle.action()))
class Bug:
def __str__(self):
return 'a bug'
def action(self):
return 'eats it'
# 类FrogWorld是一个抽象工厂,其主要职责是创建游戏的主人公和障碍物。
# 区分创建方法并使其名字通用(比如,make_character()和make_obstacle()),
# 这让我们可以动态改变当前激活的工厂(也因此改变了当前激活的游戏),而无需进行任何代码变更。
class FrogWorld:
def __init__(self, name):
print(self)
self.player_name = name
def __str__(self):
return '\n\n\t------ Frog World ———'
def make_character(self):
return Frog(self.player_name)
def make_obstacle(self):
return Bug()
# 男巫类Wizard
class Wizard:
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
# 男巫类Wizard和怪兽类Ork两者之间的交互
def interact_with(self, obstacle):
print('{} the Wizard battles against {} and {}!'.format(self, obstacle, obstacle.action()))
# 怪兽类Ork
class Ork:
def __str__(self):
return 'an evil ork'
def action(self):
return 'kills it'
# 抽象工厂,生成实例
class WizardWorld:
def __init__(self, name):
print(self)
self.player_name = name
def __str__(self):
return '\n\n\t------ Wizard World ———'
def make_character(self):
return Wizard(self.player_name)
def make_obstacle(self):
return Ork()
# 类GameEnvironment是我们游戏的主入口。它接受factory作为输入,
# 用其创建游戏的世界。方法play()则会启动hero和obstacle之间的交互
class GameEnvironment:
def __init__(self, factory):
self.hero = factory.make_character()
self.obstacle = factory.make_obstacle()
def play(self):
self.hero.interact_with(self.obstacle)
# 函数validate_age()提示用户提供一个有效的年龄。如果年龄无效,则会返回一个元组, 其第一个元素设置为False。
# 如果年龄没问题,元素的第一个元素则设置为True,但我们真正关 心的是元素的第二个元素,也就是用户提供的年龄,
def validate_age(name):
try:
age = input('Welcome {}. How old are you? '.format(name))
age = int(age)
except ValueError as err:
print("Age {
} is invalid, please try \
again…".format(age))
return (False, age)
return (True, age)
# 该函数请求用户的姓名和年龄,并根据用户的年龄决定该玩 哪个游戏
def main():
name = input("Hello. What's your name? ")
valid_input = False
while not valid_input:
valid_input, age = validate_age(name)
game = FrogWorld if age < 18 else WizardWorld
environment = GameEnvironment(game(name))
environment.play()
if __name__ == '__main__':
main()
# coding: utf-8
class Computer:
def __init__(self, serial_number):
self.serial = serial_number
self.memory = None # 单位为GB
self.hdd = None # 单位为GB
self.gpu = None
def __str__(self):
info = ('Memory: {}GB'.format(self.memory),
'Hard Disk: {}GB'.format(self.hdd),
'Graphics Card: {}'.format(self.gpu))
return '\n'.join(info)
# 引入了一个建造者ComputerBuilder、一个指挥者HardwareEngineer
# 以及一步接一步装配一台电脑的过程,这样现在就支持不同的配置了
class ComputerBuilder:
def __init__(self):
self.computer = Computer('AG23385193')
def configure_memory(self, amount):
self.computer.memory = amount
def configure_hdd(self, amount):
self.computer.hdd = amount
def configure_gpu(self, gpu_model):
self.computer.gpu = gpu_model
class HardwareEngineer:
def __init__(self):
self.builder = None
def construct_computer(self, memory, hdd, gpu):
self.builder = ComputerBuilder()
[step for step in (self.builder.configure_memory(memory),
self.builder.configure_hdd(hdd),
self.builder.configure_gpu(gpu))]
@property
def computer(self):
return self.builder.computer
def main():
engineer = HardwareEngineer()
engineer.construct_computer(hdd=500, memory=8, gpu='GeForce GTX 650 Ti')
computer = engineer.computer
print(computer)
if __name__ == '__main__':
main()
# coding: utf-8
from enum import Enum # 枚举
import time
PizzaProgress = Enum('PizzaProgress', 'queued preparation baking ready') # 准备阶段
PizzaDough = Enum('PizzaDough', 'thin thick') # 生面团
PizzaSauce = Enum('PizzaSauce', 'tomato creme_fraiche') # 调味料
PizzaTopping = Enum('PizzaTopping', 'mozzarella double_mozzarella bacon ham mushrooms red_onion oregano') # 配料
STEP_DELAY = 3 # 考虑是示例,单位为秒
class Pizza:
def __init__(self, name):
self.name = name
self.dough = None
self.sauce = None
self.topping = []
def __str__(self):
return self.name
# 这个本来不用放在这里的,但是生面团都一样,因此这里提高代码利用率放在这里
def prepare_dough(self, dough):
self.dough = dough
print('preparing the {} dough of your {}...'.format(self.dough.name, self))
time.sleep(STEP_DELAY)
print('done with the {} dough'.format(self.dough.name))
# 建造者:制作玛格丽特比萨(MargaritaBudiler),
class MargaritaBuilder:
def __init__(self):
self.pizza = Pizza('margarita')
self.progress = PizzaProgress.queued
self.baking_time = 5 # 考虑是示例,单位为秒
def prepare_dough(self):
self.progress = PizzaProgress.preparation
self.pizza.prepare_dough(PizzaDough.thin)
def add_sauce(self):
print('adding the tomato sauce to your margarita...')
self.pizza.sauce = PizzaSauce.tomato
time.sleep(STEP_DELAY)
print('done with the tomato sauce')
def add_topping(self):
print('adding the topping (double mozzarella, oregano) to your margarita')
self.pizza.topping.append([i for i in
(PizzaTopping.double_mozzarella, PizzaTopping.oregano)])
time.sleep(STEP_DELAY)
print('done with the topping (double mozzarrella, oregano)')
def bake(self):
self.progress = PizzaProgress.baking
print('baking your margarita for {} seconds'.format(self.baking_time))
time.sleep(self.baking_time)
self.progress = PizzaProgress.ready
print('your margarita is ready')
# 建造者:制作奶油熏肉比萨(CreamyBaconBuilder)。
class CreamyBaconBuilder:
def __init__(self):
self.pizza = Pizza('creamy bacon')
self.progress = PizzaProgress.queued
self.baking_time = 7 # 考虑是示例,单位为秒
def prepare_dough(self):
self.progress = PizzaProgress.preparation
self.pizza.prepare_dough(PizzaDough.thick)
def add_sauce(self):
print('adding the crème fraîche sauce to your creamy bacon')
self.pizza.sauce = PizzaSauce.creme_fraiche
time.sleep(STEP_DELAY)
print('done with the crème fraîche sauce')
def add_topping(self):
print('adding the topping (mozzarella, bacon, ham, mushrooms, red onion, oregano) to your creamy bacon')
self.pizza.topping.append([t for t in
(PizzaTopping.mozzarella, PizzaTopping.bacon,
PizzaTopping.ham, PizzaTopping.mushrooms,
PizzaTopping.red_onion, PizzaTopping.oregano)])
time.sleep(STEP_DELAY)
print('done with the topping (mozzarella, bacon, ham, mushrooms, red onion, oregano)')
def bake(self):
self.progress = PizzaProgress.baking
print('baking your creamy bacon for {} seconds'.format(self.baking_time))
time.sleep(self.baking_time)
self.progress = PizzaProgress.ready
print('your creamy bacon is ready')
# 指挥者就是服务员。Waiter类的核心是construct_pizza方法,
# 该方法接受一个建造者作为参数,并以正确的顺序执行比萨的所有准备步骤。
class Waiter:
def __init__(self):
self.builder = None
def construct_pizza(self, builder):
self.builder = builder
[step() for step in (builder.prepare_dough,
builder.add_sauce, builder.add_topping, builder.bake)]
@property
def pizza(self):
return self.builder.pizza
# 函数validate_style()类似于第1章中描述的validate_age()函数,用于确保用户提供有效的输入,
# 当前案例中这个输入是映射到一个比萨建造者的字符;输入字符m表示使用 MargaritaBuilder类,
# 输入字符c则使用CreamyBaconBuilder类。
# 这些映射关系存储在参数 builder中。该函数会返回一个元组,如果输入有效,则元组的第一个元素被设置为True,否则为False,
def validate_style(builders):
try:
pizza_style = input('What pizza would you like, [m]argarita or [c]reamy bacon? ')
builder = builders[pizza_style]()
valid_input = True
except KeyError as err:
print('Sorry, only margarita (key m) and creamy bacon (key c) are available')
return (False, None)
return (True, builder)
def main():
builders = dict(m=MargaritaBuilder, c=CreamyBaconBuilder)
valid_input = False
while not valid_input:
valid_input, builder = validate_style(builders)
print()
waiter = Waiter()
waiter.construct_pizza(builder)
pizza = waiter.pizza
print()
print('Enjoy your {}!'.format(pizza))
if __name__ == '__main__':
main()
# coding: utf-8
class Pizza:
def __init__(self, builder):
self.garlic = builder.garlic
self.extra_cheese = builder.extra_cheese
def __str__(self):
garlic = 'yes' if self.garlic else 'no'
cheese = 'yes' if self.extra_cheese else 'no'
info = ('Garlic: {}'.format(garlic), 'Extra cheese: {}'.format(cheese))
return '\n'.join(info)
class PizzaBuilder:
def __init__(self):
self.extra_cheese = False
self.garlic = False
def add_garlic(self):
self.garlic = True
return self
def add_extra_cheese(self):
self.extra_cheese = True
return self
def build(self):
return Pizza(self)
if __name__ == '__main__':
pizza = Pizza.PizzaBuilder().add_garlic().add_extra_cheese().build()
print(pizza)
# coding: utf-8
import copy
from collections import OrderedDict
class Book:
# 仅有三个形参是固定的:name、authors和price,
# v但是使用rest变长列表,调用者 能以关键词的形式(名称=值)传入更多的参数。
# self.__dict__.update(rest)一行将rest 的内容添加到Book类的内部字典中,成为它的一部分。
def __init__(self, name, authors, price, **rest):
'''rest的例子有:出版商,长度,标签,出版日期'''
self.name = name
self.authors = authors
self.price = price # 单位为美元
self.__dict__.update(rest)
# 我们并不知道所有被添加参数的名称,但又需要访问内部字典将这些参数应用到__str__()中,
# 并且字典的内容并不遵循任何特定的顺序,所以使用一个OrderedDict来强制元素有序,
# 否则,每次程序执行都会产生不同的输出。
def __str__(self):
mylist = []
ordered = OrderedDict(sorted(self.__dict__.items()))
for i in ordered.keys():
mylist.append('{}: {}'.format(i, ordered[i]))
if i == 'price':
mylist.append('$')
mylist.append('\n')
return ''.join(mylist)
# Prototype类实现了原型设计模式。Prototype类的核心是clone()方法,该方法使用我们 熟悉的copy.deepcopy()函数来完成真正的克隆工作。
# 但Prototype类在支持克隆之外做了一点更多的事情,它包含了方法register()和unregister(),这两个方法用于在一个字典中追 踪被克隆的对象。
class Prototype:
def __init__(self):
self.objects = dict()
def register(self, identifier, obj):
self.objects[identifier] = obj
def unregister(self, identifier):
del self.objects[identifier]
def clone(self, identifier, **attr):
found = self.objects.get(identifier)
if not found:
raise ValueError('Incorrect object identifier: {}'.format(identifier))
obj = copy.deepcopy(found)
obj.__dict__.update(attr)
return obj
def main():
b1 = Book('The C Programming Language', ('Brian W. Kernighan', 'Dennis M.Ritchie'), price=118, publisher='Prentice Hall',
length=228, publication_date='1978-02-22', tags=('C', 'programming', 'algorithms', 'data structures'))
prototype = Prototype()
cid = 'k&r-first'
prototype.register(cid, b1)
b2 = prototype.clone(cid, name='The C Programming Language(ANSI)', price=48.99,
length=274, publication_date='1988-04-01', edition=2)
for i in (b1, b2):
print(i)
print('ID b1 : {} != ID b2 : {}'.format(id(b1), id(b2)))
if __name__ == '__main__':
main()
简便记忆:外观(装饰器)代理(控制器)享元(适配器)
# coding: utf-8
from enum import Enum
from abc import ABCMeta, abstractmethod
State = Enum('State', 'new running sleeping restart zombie')
class User:
pass
class Process:
pass
class File:
pass
class Server(metaclass=ABCMeta):
@abstractmethod
def __init__(self):
pass
def __str__(self):
return self.name
@abstractmethod
def boot(self):
pass
@abstractmethod
def kill(self, restart=True):
pass
class FileServer(Server):
def __init__(self):
'''初始化文件服务进程要求的操作'''
self.name = 'FileServer'
self.state = State.new
def boot(self):
print('booting the {}'.format(self))
'''启动文件服务进程要求的操作'''
self.state = State.running
def kill(self, restart=True):
print('Killing {}'.format(self))
'''杀死文件服务进程要求的操作'''
self.state = State.restart if restart else State.zombie
def create_file(self, user, name, permissions):
'''检查访问权限的有效性、用户权限,等等'''
print("trying to create the file '{}' for user '{}' with permissions {}".format(name, user, permissions))
class ProcessServer(Server):
def __init__(self):
'''初始化进程服务进程要求的操作'''
self.name = 'ProcessServer'
self.state = State.new
def boot(self):
print('booting the {}'.format(self))
'''启动进程服务进程要求的操作'''
self.state = State.running
def kill(self, restart=True):
print('Killing {}'.format(self))
'''杀死进程服务进程要求的操作'''
self.state = State.restart if restart else State.zombie
def create_process(self, user, name):
'''检查用户权限、生成PID,等等'''
print("trying to create the process '{}' for user '{}'".format(name, user))
class WindowServer:
pass
class NetworkServer:
pass
class OperatingSystem:
'''外观'''
def __init__(self):
self.fs = FileServer()
self.ps = ProcessServer()
def start(self):
[i.boot() for i in (self.fs, self.ps)]
def create_file(self, user, name, permissions):
return self.fs.create_file(user, name, permissions)
def create_process(self, user, name):
return self.ps.create_process(user, name)
def main():
os = OperatingSystem()
os.start()
os.create_file('foo', 'hello', '-rw-r-r')
os.create_process('bar', 'ls /tmp')
if __name__ == '__main__':
main()
## 1、直接使用递归,函数执行时间
def fibonacci(n):
assert(n >= 0), 'n must be >= 0'
return n if n in (0, 1) else fibonacci(n-1) + fibonacci(n-2)
if __name__ == '__main__':
from timeit import Timer
t = Timer('fibonacci(8)', 'from __main__ import fibonacci')
print(t.timeit())
# 结果16s多
## 使用dict缓存计算结果
known = {
0:0, 1:1}
def fibonacci(n):
assert(n >= 0), 'n must be >= 0'
if n in known:
return known[n]
res = fibonacci(n-1) + fibonacci(n-2)
known[n] = res
return res
if __name__ == '__main__':
from timeit import Timer
t = Timer('fibonacci(100)', 'from __main__ import fibonacci')
print(t.timeit())
# 结果:0.3s多
## 使用装饰器
# 函数functools.wraps() 是一个为创建修饰器提供便利的函数;虽不强制,但推荐使用,
# 因为它能保留被修饰函数的文档和签名。这种情况要求参数列表*args,因为被修饰的函数可能有 输入参数。
import functools
def memoize(fn):
known = dict()
@functools.wraps(fn)
def memoizer(*args):
if args not in known:
known[args] = fn(*args)
return known[args]
return memoizer
# coding: utf-8
import functools
def memoize(fn):
known = dict()
@functools.wraps(fn)
def memoizer(*args):
if args not in known:
known[args] = fn(*args)
return known[args]
return memoizer
@memoize
def nsum(n):
'''返回前n个数字的和'''
assert(n >= 0), 'n must be >= 0'
return 0 if n == 0 else n + nsum(n-1)
@memoize
def fibonacci(n):
'''返回斐波那契数列的第n个数'''
assert(n >= 0), 'n must be >= 0'
return n if n in (0, 1) else fibonacci(n-1) + fibonacci(n-2)
if __name__ == '__main__':
from timeit import Timer
measure = [{
'exec': 'fibonacci(100)', 'import': 'fibonacci',
'func': fibonacci}, {
'exec': 'nsum(200)', 'import': 'nsum',
'func': nsum}]
for m in measure:
t = Timer('{}'.format(m['exec']), 'from __main__ import \
{
}'.format(m['import']))
print('name: {
}, doc: {
}, executing: {
}, time: \
{
}'.format(m['func'].__name__, m['func'].__doc__,
m['exec'], t.timeit()))
# coding: utf-8
"""
LazyProperty类实际上是一个描述符(请参考网页[t.cn/RqrYBND])。描述符(descriptor)
是Python中重写类属性访问方法(__get__()、__set__()和__delete__())的默认行为要使
用的一种推荐机制。LazyProperty类仅重写了__set__(),因为这是其需要重写的唯一访问方
法。换句话说,我们无需重写所有访问方法。__get__()方法所访问的特性值,正是下层方法想
要赋的值,并使用setattr()来手动赋值。__get__()实际做的事情非常简单,就是使用值来
替代方法!这意味着不仅特性是惰性加载的,而且仅可以设置一次。
"""
class LazyProperty:
def __init__(self, method):
self.method = method
self.method_name = method.__name__
# print('function overriden: {}'.format(self.method))
# print("function's name: {}".format(self.method_name))
def __get__(self, obj, cls):
if not obj:
return None
value = self.method(obj) # 获取obj对象的method属性的值么?
# print('value {}'.format(value))
setattr(obj, self.method_name, value)
return value
class Test:
def __init__(self):
self.x = 'foo'
self.y = 'bar'
self._resource = None
@LazyProperty
def resource(self):
print('initializing self._resource which is: {}'.format(self._resource))
self._resource = tuple(range(5)) # 代价大的
return self._resource
def main():
t = Test()
print(t.x)
print(t.y)
# 做更多的事情。。。
print(t.resource)
print(t.resource)
if __name__ == '__main__':
main()
class A:
def __init__(self, val):
self.val = val
def __get__(self, instance, owner):
print('self is ', self)
print('instance is ', instance)
print('owner is ', owner)
return self.val
class B:
a = A(10)
b = B()
print(b.a)
# descriptor
# 一个类中定义了__set__, __get__和__delete__中的一个或多个就是descriptor。
# __get__函数定义的时候需要的参数为三个,分别为self, instance, owner。
# __set__函数定义的时候需要的参数为三个,分别是self, instance, value。
# 理解了下面的一句话就理解了文件描述器的实质:
# self很简单,就是descriptor的实例;
# instance就是descriptor所在类中的实例;
# owner指的是descriptor所在的类。
# 还可以看出来的是,打印的b.a不是一个是实例对象,而是通过调用a.__get__()函数返回的值。
# 所以我感觉这个descriptor叫做描述器(借鉴于decorator)似乎更合适。。。
# 因为这个descriptor似乎是用来描述所在的实例的。。。
# 总结:通过实例调用(b.a)这个descriptor对象,返回的并不是这个descriptor(你可以尝试打印一个实例对象,
# 看看返回的是什么),而是该descriptor调用__get__返回的值,所以这个a像是b的描述器。
class LazyProperty(object):
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
if instance is None:
return self
else:
value = self.func(instance)
setattr(instance, self.func.__name__, value)
return value
class A:
@LazyProperty
def func(self):
time.sleep(1)
return 10
# coding: utf-8
"""
查看用户列表:这一操作不要求特殊权限。
添加新用户:这一操作要求客户端提供一个特殊的密码。
"""
class SensitiveInfo:
def __init__(self):
self.users = ['nick', 'tom', 'ben', 'mike']
def read(self):
print('There are {} users: {}'.format(len(self.users), ' '.join(self.users)))
def add(self, user):
self.users.append(user)
print('Added user {}'.format(user))
class Info:
'''SensitiveInfo的保护代理'''
def __init__(self):
self.protected = SensitiveInfo()
self.secret = '0xdeadbeef'
def read(self):
self.protected.read()
def add(self, user):
sec = input('what is the secret? ')
self.protected.add(user) if sec == self.secret else print("That's wrong!")
def main():
info = Info()
while True:
print('1. read list |==| 2. add user |==| 3. quit')
key = input('choose option: ')
if key == '1':
info.read()
elif key == '2':
name = input('choose username: ')
info.add(name)
elif key == '3':
exit()
else:
print('unknown option: {}'.format(key))
if __name__ == '__main__':
main()
"""
用户输入一个数字,然后就能看到与这个数字相关的名人名言。
名人名言存储在一个quotes元组中。这种数据通常是存储在数据库、文件或其他地方,
只有模型能够直接访问它。
"""
quotes = ('A man is not complete until he is married. Then he is finished.',
'As I said before, I never repeat myself.',
'Behind a successful man is an exhausted woman.',
'Black holes really suck...', 'Facts are stubborn things.')
class QuoteModel:
def get_quote(self, n):
try:
value = quotes[n]
except IndexError as err:
value = 'Not found!'
return value
class QuoteTerminalView:
def show(self, quote):
print('And the quote is: "{}"'.format(quote))
def error(self, msg):
print('Error: {}'.format(msg))
def select_quote(self):
return input('Which quote number would you like to see?')
class QuoteTerminalController:
def __init__(self):
self.model = QuoteModel()
self.view = QuoteTerminalView()
def run(self):
valid_input = False
while not valid_input:
n = self.view.select_quote()
try:
n = int(n)
except ValueError as err:
self.view.error("Incorrect index '{}'".format(n))
else:
valid_input = True
quote = self.model.get_quote(n)
self.view.show(quote)
def main():
controller = QuoteTerminalController()
while True:
controller.run()
if __name__ == '__main__':
main()
# coding: utf-8
"""
构造一小片水果树的森林,小到能确保在单个终端页面中阅读整个输出。
然而,无论你构造的森林有多大,内存分配都保持相同。
"""
import random
from enum import Enum
# 枚举
TreeType = Enum('TreeType', 'apple_tree cherry_tree peach_tree')
class Tree:
# pool是一个类属性(类的所有实例共享的一个变量),(换句话说,是我们的缓存)
pool = dict()
# 使用特殊方法__new__(这个方法在__init__之前被调用),
# 我们把Tree类变换成一个元类,元类支持自引用。这意味着cls引用的是Tree类。
# 当客户端要创建Tree的一个实例时,会以tree_type参数传递树的种类。
# 树的种类用于检查是否创建过相同种类的树。如果是,则返回之前创建的对象;
# 否则,将这个新的树种添加到池中,并返回相应的新对象
def __new__(cls, tree_type):
obj = cls.pool.get(tree_type, None)
if not obj:
obj = object.__new__(cls) # 实例化,对象.方法(类)
cls.pool[tree_type] = obj # 将对象加入类的pool中
obj.tree_type = tree_type # 设置对象的属性
return obj
# 元类编程中obj是本类实例化的对象
# cls是类
# 方法render()用于在屏幕上渲染一棵树。注意,享元不知道的所有可变(外部的)信息都需要由客户端代码显式地传递。
# 在当前案例中,每棵树都用到一个随机的年龄和一个x, y形式的位置。
# 为了让render()更加有用,有必要确保没有树会被渲染到另一个棵之上。
def render(self, age, x, y):
print('render a tree of type {} and age {} at ({}, {})'.format(self.tree_type, age, x, y))
# main()函数展示了我们可以如何使用享元模式。一棵树的年龄是1到30年之间的一个随机值。
# 坐标使用1到100之间的随机值。虽然渲染了18棵树,但仅分配了3棵树的内存。
# 输出的最后一行证明当使用享元时,我们不能依赖对象的ID。函数id()会返回对象的内存地址。
# Python规范并没有要求id()返回对象的内存地址,只是要求id()为每个对象返回一个唯一性ID,
# 不过CPython(Python的官方实现)正好使用对象的内存地址作为对象唯一性ID。
# 在我们的例子中,即使两个对象看起来不相同,但是如果它们属于同一个享元家族(在这里,家族由tree_type定义),那么它们实际上有相同的ID。
# 当然,不同ID的比较仍然可用于不同家族的对象,但这仅在客户端知道实现细节的情况下才可行(通常并非如此)。
def main():
rnd = random.Random()
age_min, age_max = 1, 30 # 单位为年
min_point, max_point = 0, 100
tree_counter = 0
for _ in range(10):
t1 = Tree(TreeType.apple_tree)
t1.render(rnd.randint(age_min, age_max),
rnd.randint(min_point, max_point),
rnd.randint(min_point, max_point))
tree_counter += 1
for _ in range(3):
t2 = Tree(TreeType.cherry_tree)
t2.render(rnd.randint(age_min, age_max),
rnd.randint(min_point, max_point),
rnd.randint(min_point, max_point))
tree_counter += 1
for _ in range(5):
t3 = Tree(TreeType.peach_tree)
t3.render(rnd.randint(age_min, age_max),
rnd.randint(min_point, max_point),
rnd.randint(min_point, max_point))
tree_counter += 1
print('trees rendered: {}'.format(tree_counter))
print('trees actually created: {}'.format(len(Tree.pool)))
t4 = Tree(TreeType.cherry_tree)
t5 = Tree(TreeType.cherry_tree)
t6 = Tree(TreeType.apple_tree)
print('{} == {}? {}'.format(id(t4), id(t5), id(t4) == id(t5)))
print('{} == {}? {}'.format(id(t5), id(t6), id(t5) == id(t6)))
if __name__ == '__main__':
main()
# 外部模块:external.py
class Synthesizer:
def __init__(self, name):
self.name = name
def __str__(self):
return 'the {} synthesizer'.format(self.name)
def play(self):
return 'is playing an electronic song'
class Human:
def __init__(self, name):
self.name = name
def __str__(self):
return '{} the human'.format(self.name)
def speak(self):
return 'says hello'
# 自己的代码adapter.py
from external import Synthesizer, Human
class Computer:
def __init__(self, name):
self.name = name
def __str__(self):
return 'the {} computer'.format(self.name)
def execute(self):
return 'executes a program'
# 客户端仅知道如何调用execute()方法,并不知道play() 4 和speak()。
# 在不改变Synthesizer和Human类的前提下,我们该如何做才能让代码有效?适配器是救星!
# 我们创建一个通用的Adapter类,将一些带不同接口的对象适配到一个统一接口中。
# __init__()方法的obj参数是我们想要适配的对象,adapted_methods是一个字典,键值对中的键是客户端要调用的方法,值是应该被调用的方法。
class Adapter:
def __init__(self, obj, adapted_methods):
self.obj = obj
self.__dict__.update(adapted_methods)
def __str__(self):
return str(self.obj)
def main():
objects = [Computer('Asus')]
synth = Synthesizer('moog')
objects.append(Adapter(synth, dict(execute=synth.play)))
human = Human('Bob')
objects.append(Adapter(human, dict(execute=human.speak)))
for i in objects:
print('{} {}'.format(str(i), i.execute()))
if __name__ == "__main__":
main()
简便记忆:观察者命令解释器将责任链、状态机做成策略模板
# 基类Publisher,包括添加、删除及通知观察者这些公用功能。
# 观察者们保存在列表observers中。add()方法注册一个新的观察者,或者在该观察者已存在时引发一个错误。
# remove()方法注销一个已有观察者,或者在该观察者尚未存在时引发一个错误。
# 最后,notify()方法则在变化发生时通知所有观察者。
class Publisher:
def __init__(self):
self.observers = []
def add(self, observer):
if observer not in self.observers:
self.observers.append(observer)
else:
print('Failed to add: {}'.format(observer))
def remove(self, observer):
try:
self.observers.remove(observer)
except ValueError:
print('Failed to remove: {}'.format(observer))
def notify(self):
[o.exec(self) for o in self.observers]
# efaultFormatter类继承自Publisher,并添加格式化程序特定的功能。
class DefaultFormatter(Publisher):
# __init__()做的第一件事情就是调用基类的__init__()方法
# DefaultFormatter实例有自己的名字,这样便于我们跟踪其状态。
def __init__(self, name):
Publisher.__init__(self)
self.name = name
self._data = 0
# __str__()方法返回关于发布者名称和_data值的信息。
# type(self).__name是一种获取类名的方便技巧,避免硬编码类名。
def __str__(self):
return "{}: '{}' has data = {}".format(type(self).__name__, self.name, self._data)
# @property修饰器来提供_data变量的读访问方式。这样,我们就能使用object.data来替代object.data()。
@property
def data(self):
return self._data
# @setter该修饰器会在每次使用赋值操作符(=)为_data变量赋新值时被调用。
@data.setter
def data(self, new_value):
try:
self._data = int(new_value)
except ValueError as e:
print('Error: {}'.format(e))
else:
self.notify()
# 两个观察者:HexFormatter和BinaryFormatter的功能非常相似。
# 唯一的不同在于如何格式化从发布者那获取到的数据值,即分别以十六进制和二进制进行格式化。
class HexFormatter:
def exec(self, publisher):
print("{}: '{}' has now hex data = {}".format(type(self).__name__,
publisher.name, hex(publisher.data)))
class BinaryFormatter:
def exec(self, publisher):
print("{}: '{}' has now bin data = {}".format(type(self).__name__,
publisher.name, bin(publisher.data)))
def main():
df = DefaultFormatter('test1')
print(df)
print()
hf = HexFormatter()
df.add(hf)
df.data = 3
print(df)
print()
bf = BinaryFormatter()
df.add(bf)
df.data = 21
print(df)
print()
df.remove(hf)
df.data = 40
print(df)
print()
df.remove(hf)
df.add(bf)
df.data = 'hello'
print(df)
print()
df.data = 15.8
print(df)
if __name__ == '__main__':
main()
import os
verbose = True
# 文件改名
class RenameFile:
def __init__(self, path_src, path_dest):
self.src, self.dest = path_src, path_dest
def execute(self):
if verbose:
print("[renaming '{}' to '{}']".format(self.src, self.dest))
os.rename(self.src, self.dest)
def undo(self):
if verbose:
print("[renaming '{}' back to '{}']".format(self.dest, self.src))
os.rename(self.dest, self.src)
# 创建文件
class CreateFile:
def __init__(self, path, txt='hello world\n'):
self.path, self.txt = path, txt
def execute(self):
if verbose:
print("[creating file '{}']".format(self.path))
with open(self.path, mode='w', encoding='utf-8') as out_file:
out_file.write(self.txt)
def undo(self):
delete_file(self.path)
# 读取文件不支持回滚
class ReadFile:
def __init__(self, path):
self.path = path
def execute(self):
if verbose:
print("[reading file '{}']".format(self.path))
with open(self.path, mode='r', encoding='utf-8') as in_file:
print(in_file.read(), end='')
# 删除文件也不支持回滚
def delete_file(path):
if verbose:
print("deleting file '{}'".format(path))
os.remove(path)
def main():
orig_name, new_name = 'file1', 'file2'
commands = []
for cmd in CreateFile(orig_name), ReadFile(orig_name), RenameFile(orig_name, new_name):
commands.append(cmd)
[c.execute() for c in commands]
answer = input('reverse the executed commands? [y/n] ')
if answer not in 'yY':
print("the result is {}".format(new_name))
exit()
for c in reversed(commands):
try:
c.undo()
except AttributeError as e:
pass
if __name__ == '__main__':
main()
# coding: utf-8
"""
创建一种内部DSL控制一个智能屋
一个事件的形式为command -> receiver -> arguments。参数部分是可选的。
并不是所有事件都要求参数。不要求任何参数的事件例子如下所示。
open -> gate
->符号用于标记事件一个部分的结束,并声明下一个部分的开始。
Pyparsing是python中的一个模块
使用巴科斯-诺尔形式(Backus-Naur Form,BNF)表示法来定义语法
event ::= command token receiver token arguments
command ::= word+
word ::= a collection of one or more alphanumeric characters
token ::= ->
receiver ::= word+
arguments ::= word+
实际的代码:
word = Word(alphanums)
command = Group(OneOrMore(word))
token = Suppress("->")
device = Group(OneOrMore(word))
argument = Group(OneOrMore(word))
event = command + token + device + Optional(token + argument)
"""
from pyparsing import Word, OneOrMore, Optional, Group, Suppress, alphanums
class Gate:
def __init__(self):
self.is_open = False
def __str__(self):
return 'open' if self.is_open else 'closed'
def open(self):
print('opening the gate')
self.is_open = True
def close(self):
print('closing the gate')
self.is_open = False
class Garage:
def __init__(self):
self.is_open = False
def __str__(self):
return 'open' if self.is_open else 'closed'
def open(self):
print('opening the garage')
self.is_open = True
def close(self):
print('closing the garage')
self.is_open = False
class Aircondition:
def __init__(self):
self.is_on = False
def __str__(self):
return 'on' if self.is_on else 'off'
def turn_on(self):
print('turning on the aircondition')
self.is_on = True
def turn_off(self):
print('turning off the aircondition')
self.is_on = False
class Heating:
def __init__(self):
self.is_on = False
def __str__(self):
return 'on' if self.is_on else 'off'
def turn_on(self):
print('turning on the heating')
self.is_on = True
def turn_off(self):
print('turning off the heating')
self.is_on = False
class Boiler:
def __init__(self):
self.temperature = 83 # in celsius
def __str__(self):
return 'boiler temperature: {}'.format(self.temperature)
def increase_temperature(self, amount):
print("increasing the boiler's temperature by {} degrees".format(amount))
self.temperature += amount
def decrease_temperature(self, amount):
print("decreasing the boiler's temperature by {} degrees".format(amount))
self.temperature -= amount
class Fridge:
def __init__(self):
self.temperature = 2 # 单位为摄氏度
def __str__(self):
return 'fridge temperature: {}'.format(self.temperature)
def increase_temperature(self, amount):
print("increasing the fridge's temperature by {} degrees".format(amount))
self.temperature += amount
def decrease_temperature(self, amount):
print("decreasing the fridge's temperature by {} degrees".format(amount))
self.temperature -= amount
def main():
word = Word(alphanums)
command = Group(OneOrMore(word))
token = Suppress("->")
device = Group(OneOrMore(word))
argument = Group(OneOrMore(word))
event = command + token + device + Optional(token + argument)
gate = Gate()
garage = Garage()
airco = Aircondition()
heating = Heating()
boiler = Boiler()
fridge = Fridge()
tests = ('open -> gate',
'close -> garage',
'turn on -> aircondition',
'turn off -> heating',
'increase -> boiler temperature -> 5 degrees',
'decrease -> fridge temperature -> 2 degrees')
open_actions = {
'gate': gate.open,
'garage': garage.open,
'aircondition': airco.turn_on,
'heating': heating.turn_on,
'boiler temperature': boiler.increase_temperature,
'fridge temperature': fridge.increase_temperature}
close_actions = {
'gate': gate.close,
'garage': garage.close,
'aircondition': airco.turn_off,
'heating': heating.turn_off,
'boiler temperature': boiler.decrease_temperature,
'fridge temperature': fridge.decrease_temperature}
"""
获取Pyparsing解析结果的最简单方式是使用parseString()方法,该方法返回的结果是一
个ParseResults实例,它实际上是一个可视为嵌套列表的解析树。例如,执行print(event.
parseString('increase -> boiler temperature -> 3 degrees'))得到的结果如下所示。
[['increase'], ['boiler', 'temperature'], ['3', 'degrees']]
"""
for t in tests:
if len(event.parseString(t)) == 2: # 没有参数
cmd, dev = event.parseString(t)
cmd_str, dev_str = ' '.join(cmd), ' '.join(dev)
if 'open' in cmd_str or 'turn on' in cmd_str:
open_actions[dev_str]()
elif 'close' in cmd_str or 'turn off' in cmd_str:
close_actions[dev_str]()
elif len(event.parseString(t)) == 3: # 有参数
cmd, dev, arg = event.parseString(t)
cmd_str, dev_str, arg_str = ' '.join(cmd), ' '.join(dev), ' '.join(arg)
num_arg = 0
try:
num_arg = int(arg_str.split()[0]) # 抽取数值部分
except ValueError as err:
print("expected number but got: '{}'".format(arg_str[0]))
if 'increase' in cmd_str and num_arg > 0:
open_actions[dev_str](num_arg)
elif 'decrease' in cmd_str and num_arg > 0:
close_actions[dev_str](num_arg)
if __name__ == '__main__':
main()
#!/usr/bin/python
#coding:utf8
class Handler:
def successor(self, successor):
self.successor = successor
class ConcreteHandler1(Handler):
def handle(self, request):
if request > 0 and request <= 10:
print("in handler1")
else:
self.successor.handle(request)
class ConcreteHandler2(Handler):
def handle(self, request):
if request > 10 and request <= 20:
print("in handler2")
else:
self.successor.handle(request)
class ConcreteHandler3(Handler):
def handle(self, request):
if request > 20 and request <= 30:
print("in handler3")
else:
print('end of chain, no handler for {}'.format(request))
class Client:
def __init__(self):
h1 = ConcreteHandler1()
h2 = ConcreteHandler2()
h3 = ConcreteHandler3()
h1.successor(h2)
h2.successor(h3)
requests = [2, 5, 14, 22, 18, 3, 35, 27, 20]
for request in requests:
h1.handle(request)
if __name__ == "__main__":
client = Client()
from state_machine import State, Event, acts_as_state_machine, after, before, InvalidStateTransition
@acts_as_state_machine
class Process:
created = State(initial=True)
waiting = State()
running = State()
terminated = State()
blocked = State()
swapped_out_waiting = State()
swapped_out_blocked = State()
wait = Event(from_states=(created, running, blocked,
swapped_out_waiting), to_state=waiting)
run = Event(from_states=waiting, to_state=running)
terminate = Event(from_states=running, to_state=terminated)
block = Event(from_states=(running, swapped_out_blocked),
to_state=blocked)
swap_wait = Event(from_states=waiting, to_state=swapped_out_waiting)
swap_block = Event(from_states=blocked, to_state=swapped_out_blocked)
def __init__(self, name):
self.name = name
@after('wait')
def wait_info(self):
print('{} entered waiting mode'.format(self.name))
@after('run')
def run_info(self):
print('{} is running'.format(self.name))
@before('terminate')
def terminate_info(self):
print('{} terminated'.format(self.name))
@after('block')
def block_info(self):
print('{} is blocked'.format(self.name))
@after('swap_wait')
def swap_wait_info(self):
print('{} is swapped out and waiting'.format(self.name))
@after('swap_block')
def swap_block_info(self):
print('{} is swapped out and blocked'.format(self.name))
def transition(process, event, event_name):
try:
event()
except InvalidStateTransition as err:
print('Error: transition of {} from {} to {} failed'.format(process.name,
process.current_state, event_name))
def state_info(process):
print('state of {}: {}'.format(process.name, process.current_state))
def main():
RUNNING = 'running'
WAITING = 'waiting'
BLOCKED = 'blocked'
TERMINATED = 'terminated'
p1, p2 = Process('process1'), Process('process2')
[state_info(p) for p in (p1, p2)]
print()
transition(p1, p1.wait, WAITING)
transition(p2, p2.terminate, TERMINATED)
[state_info(p) for p in (p1, p2)]
print()
transition(p1, p1.run, RUNNING)
transition(p2, p2.wait, WAITING)
[state_info(p) for p in (p1, p2)]
print()
transition(p2, p2.run, RUNNING)
[state_info(p) for p in (p1, p2)]
print()
[transition(p, p.block, BLOCKED) for p in (p1, p2)]
[state_info(p) for p in (p1, p2)]
print()
[transition(p, p.terminate, TERMINATED) for p in (p1, p2)]
[state_info(p) for p in (p1, p2)]
if __name__ == '__main__':
main()
"""
The state pattern allows us to change an object's behavior at runtime
— something that Python naturally excels at!
This example has a very simple radio. It has an AM/FM toggle switch,
and a scan button to scan to the next station.
"""
class State(object):
"""Base state. This is to share functionality"""
def scan(self):
"""Scan the dial to the next station"""
self.pos += 1
if self.pos == len(self.stations):
self.pos = 0
print "Scanning… Station is", self.stations[self.pos], self.name
class AmState(State):
def __init__(self, radio):
self.radio = radio
self.stations = ["1250", "1380", "1510"]
self.pos = 0
self.name = "AM"
def toggle_amfm(self):
print "Switching to FM"
self.radio.state = self.radio.fmstate
class FmState(State):
def __init__(self, radio):
self.radio = radio
self.stations = ["81.3", "89.1", "103.9"]
self.pos = 0
self.name = "FM"
def toggle_amfm(self):
print "Switching to AM"
self.radio.state = self.radio.amstate
class Radio(object):
"""A radio.
It has a scan button, and an AM/FM toggle switch."""
def __init__(self):
"""We have an AM state and an FM state"""
self.amstate = AmState(self)
self.fmstate = FmState(self)
self.state = self.amstate
def toggle_amfm(self):
self.state.toggle_amfm()
def scan(self):
self.state.scan()
# Test our radio out
radio = Radio()
actions = [radio.scan] * 2 + [radio.toggle_amfm] + [radio.scan] * 2
actions = actions * 2
for action in actions:
action()
# coding: utf-8
# 检测一个字符串中所有的字符串是否都是唯一的
import time
SLOW = 3 # 单位为秒
LIMIT = 5 # 字符数
WARNING = 'too bad, you picked the slow algorithm :('
def pairs(seq):
n = len(seq)
for i in range(n):
yield seq[i], seq[(i + 1) % n]
# 方法一:排序,然后判断相邻两个元素是否相等
def allUniqueSort(s):
if len(s) > LIMIT:
print(WARNING)
time.sleep(SLOW)
srtStr = sorted(s)
for (c1, c2) in pairs(srtStr):
if c1 == c2:
return False
return True
# 方法二:利用python中的集合来处理
def allUniqueSet(s):
if len(s) < LIMIT:
print(WARNING)
time.sleep(SLOW)
return True if len(set(s)) == len(s) else False
def allUnique(s, strategy):
return strategy(s)
def main():
while True:
word = None
while not word:
word = input('Insert word (type quit to exit)> ')
if word == 'quit':
print('bye')
return
strategy_picked = None
strategies = {
'1': allUniqueSet, '2': allUniqueSort}
while strategy_picked not in strategies.keys():
strategy_picked = input('Choose strategy: [1] Use a set, [2] Sort and pair> ')
try:
strategy = strategies[strategy_picked]
print('allUnique({}): {}'.format(word, allUnique(word, strategy)))
except KeyError as err:
print('Incorrect option: {}'.format(strategy_picked))
if __name__ == '__main__':
main()
"""
将一段文本发送给一个函数,该函数要生成一个包含该文本的横幅。
横幅有多种风格,比如点或虚线围绕文本。横幅生成器有一个默认风格,
但应该能够使用我们自己提供的风格。
"""
from cowpy import cow
def dots_style(msg):
msg = msg.capitalize()
msg = '.' * 10 + msg + '.' * 10
return msg
def admire_style(msg):
msg = msg.upper()
return '!'.join(msg)
def cow_style(msg):
msg = cow.milk_random_cow(msg)
return msg
def generate_banner(msg, style=dots_style):
print('-- start of banner --')
print(style(msg))
print('-- end of banner --\n\n')
def main():
msg = 'happy coding'
[generate_banner(msg, style) for style in (dots_style, admire_style, cow_style)]
if __name__ == '__main__':
main()
参考:
《精通python设计模式》
《二十三种设计模式及其python实现》