跳到正文
网络寻租
返回

简单python_宣告式编程

什么是宣告式编程?

image

我们编程的时候, 往往是实现某个领域的业务逻辑, 而这些业务逻辑, 往往可以用定义的方式来表述. 程序员需要做的工作是, 如何把把这些业务逻辑的表述(信息), 用写程序的方式又好又快地实现出来. 如果你是一个资深程序员, 你会发现, 自己会从: 根据需求, 命令式地写代码, 控制机器一个动作又一个动作; 慢慢转移到: 用合适的文本描述需求, 然后想办法写个简单的解释器, 把这个文本转换为可执行的程序. 这种开发程序的方式, 我们叫它 宣告式編程.

大多数情况下, 我们会受到工具的限制, 不能很自然地完成这样的一个转换工作, 如果你有遇到这样的问题, 我建议你, 使用python作为编程语言.

为什么用python?

为了能够很好地支持宣告式編程, 我们需要满足几点:

data = dict(
    name = 'me',
    address = ['shanghai', 'chengdu'],
)
data['name'] = 'halida'
data['address'].append('kunshang')
class P(): pass
p = P()
p.v # 错误! p没有v

# 我们给它设置一个:
P.v = 12

p.v # --> 12

我们具体去看一个示例吧.

示例: 配置界面

我们需要写一个简单的配置用户界面, 用户可以设置一些配置功能. 我们一开始的考虑是, 完全手写, 或者采用WYSIWYG工具, 拖拖拉拉把界面整理出来, 然后再写一个set/get的方法. 比方说, 这样写代码:

class ConfigDlg(QDialog):
    def init(self):
         self.leName = QLineEdit()
         self.sbValue = QSpinBox()
         ...

    def setUi(self, data):
         self.leName.setText(data['name'])
         self.sbValue.setValue(data['value'])
         ...

    def getUi(self):
         data = {}
         data['name'] = self.leName.text()
         data['value'] = self.sbValue.value()
         ...
         return data

但是如果需求变动了怎么办? 如果配置需要添加, 修改? 那么我们就再去修改程序或者代码. 问题是, 程序写了有一段时间了, 变得陌生起来, 除非通读一遍, 不然很容易修改错. 并且代码有点散乱, 就像上面的代码, 在setUi里面写了逻辑, 如果在getUi里面忘记写了就麻烦了.

pythonic的一个原则: Simple is better than complex. 上面写代码的方式不能反映问题的本质, 我们需求的本质信息, 是我们需要配置什么内容, 以及这些内容应该用什么方式表现出来. 在上面的例子里面, 就是name和value. 我们用python数据结构的方式, 重新整理一遍需要配置的内容:

CONFIG = (
    (unicode, 'name'), 
    (int, 'value'),
)

这样是不是很清晰?

我们还要定义类型对应的控件, 以及具体处理数据的方法:

TYPES = dict(
    unicode: (QLineEdit, 'setText', 'text'),
    int: (QSpinBox, 'setValue', 'value'),
)

现在我们需要把这样的配置转换为具体的界面, 下面就是具体转换的代码, 用命令式就很直截了当了:

class ConfigDlg(QDialog):
    def init(self):
        self.widgets = {}
        for type, name in CONFIG:
        self.widgets[name] = TYPES[type][0], type

    def setUi(self, data):
        for key, value in data.iteritems():
            w, type = self.widgets(key)
            getattr(w, types[type][1])(value)

    def getUi(self):
        data = {}
        for key, value in self.widgets.iteritems():
            w, type = value
            data[key] = getattr(w, types[type][2])()
        return data

要注意到, 我们能够这样’宣告式’地写设置内容, 有几个前提:

结论

当我们需要实现一个功能的时候, 首先去探索, 这个功能的本质信息是那些? 这样的信息中, 那些是固定的? 哪些是变动的? 然后我们根据信息的本质属性, 选择对应的处理方式. 如果信息是经常变动的, 而它们变动的范围也是可以包括在一个问题域里面, 我们可以用宣告式编程, 来把他们描述出来. 不过, 如果这个问题域足够大, 足够精深, 我们恐怕要考虑专门做一门语言来包容它们了.


分享这篇文章:

上一篇
scons介绍
下一篇
pyside介绍

评论

评论通过 GitHub 账号发布,需要先使用 GitHub 登录。