V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
ayanamist
V2EX  ›  Python

写代码不能太简单了

  •  
  •   ayanamist · 2011-01-10 17:35:06 +08:00 · 5818 次点击
    这是一个创建于 5070 天前的主题,其中的信息可能已经有所发展或是发生改变。
    项目中用了反射,直接getattr('funcname')(*args)调用,外面包裹一个try..except AttributeError,结果调用的某个函数刚好也报了AttributeError的错误,于是一直提示错误,一直找不到原因。后来加了N多logging才找到原因……
    还是不能偷懒写一起,该拆开就拆开啊……教训啊……
    26 条回复    1970-01-01 08:00:00 +08:00
    freefcw
        1
    freefcw  
       2011-01-10 18:22:50 +08:00
    - -真想说活该
    为什么要搞这么麻烦呢,我对太过抽象的东西一般不用,实在是不放心,比如C的void指针
    ayanamist
        2
    ayanamist  
    OP
       2011-01-10 18:40:12 +08:00
    @freefcw 可以节省很多时间啊,一条语句就保证可以动态的调用N多类里的方法啊~
    freefcw
        3
    freefcw  
       2011-01-10 20:00:40 +08:00
    @ayanamist 唔,不喜欢这么折腾:)
    ssword
        4
    ssword  
       2011-01-10 21:29:12 +08:00
    py的话可以lambda,ruby的话可以proc,C#有delegate,java还能拿个匿名类
    什么语言呢,感觉不至于祭出来反射吧...
    freefcw
        5
    freefcw  
       2011-01-10 22:24:12 +08:00
    @ssword 是python的说
    ayanamist
        6
    ayanamist  
    OP
       2011-01-10 23:28:14 +08:00
    @ssword 根据用户提供的命令动态的调用一个类的函数,不用反射,有什么更简洁的办法么?
    keakon
        7
    keakon  
       2011-01-10 23:57:35 +08:00
    try: f = getattr('funcname')
    except AttributeError: ...
    else: f(*args)
    ssword
        8
    ssword  
       2011-01-11 20:26:44 +08:00
    @ayanamist 一个“类”的函数,还是一个“对象”的方法呢?
    一定要动态调用一个对象的方法的话,反射是必须的(不过鸭子类型的动态语言里的“反射”其实很模糊)。
    但是仅仅是为不同的输入选择不同的行为的话,感觉还是lambda更好。比如拿个dict里面放针对不同操作的函数(参数最好一样,不然肯定自找麻烦),这样就行了:
    func = actions[act_name]
    if (func): func(*arg)
    ayanamist
        9
    ayanamist  
    OP
       2011-01-11 21:35:39 +08:00
    @ssword 你这样还要手动添加列表。我是访问一个类的实例里的方法,自动根据某字符串前面几行的情况调用方法。参数是一致的,不过方法很多,而且经常修改。(其实就是某聊天机器人……)
    ayanamist
        10
    ayanamist  
    OP
       2011-01-11 21:36:17 +08:00
    @ssword 而且你这样也会出AttributeError的,不是if能避免的
    ssword
        11
    ssword  
       2011-01-11 22:03:27 +08:00
    重点不是if,留意下dict和lambda的用法。

    参数是一致的,不过方法很多
    ----
    不理解类为什么这么设计。建议是省去这个类,直接上函数。

    如果对一门语言足够了解,肯定能找到更优雅的解决方式。py不像java,高质量的代码是可以写的很短的
    chloerei
        12
    chloerei  
       2011-01-11 22:28:25 +08:00
    今天在《软件随想录》看到一句话:异常处理本质是一种无形的goto。

    不要太依赖异常,或者在抛出的时候塞进多些信息。
    ayanamist
        13
    ayanamist  
    OP
       2011-01-11 23:28:37 +08:00
    @ssword 求指教,我可以把完整代码给你看。这是未来的开源项目,不过现在太不稳定,经常重构,所以暂时不适合公开。已加Gtalk
    ayanamist
        14
    ayanamist  
    OP
       2011-01-11 23:36:33 +08:00
    @ssword 自己搜索一下,找到了这文章,似乎和你说的很像,不过只能塞入lambda表达式,我的函数代码很多,利用lambda表达式返回函数的话,还不如利用反射呢?
    http://stackoverflow.com/questions/1583617/what-does-lambda-mean-in-python-and-whats-the-simplest-way-to-use-it
    python新人继续求指教
    chuangbo
        15
    chuangbo  
       2011-01-11 23:40:04 +08:00
    meta class 可以解决这个问题。类在声明的时候就会执行元类的代码,就可以收集方法了。webpy 的 auto_application 就是这么实现的
    see:
    https://github.com/webpy/webpy/blob/master/web/application.py#L477
    ayanamist
        16
    ayanamist  
    OP
       2011-01-12 00:24:14 +08:00
    @chuangbo 多谢~今天头脑爆炸了,学到很多东西,由上面@ssword同学找到了Y Combinator的东西,你这里又有meta class,学不完了……TAT
    chuangbo
        17
    chuangbo  
       2011-01-12 00:41:12 +08:00
    或者更简单的,可以用decorator
    写了个简单的例子,十分好理解,用来提示help的命令的:
    http://gist.github.com/774683
    ayanamist
        18
    ayanamist  
    OP
       2011-01-12 00:48:05 +08:00
    @chuangbo 谢谢~今天(12号)晚上我再研究研究我的代码,不过我想问问的是,用反射会不会更好一些呢?
    keakon
        19
    keakon  
       2011-01-12 02:44:29 +08:00
    回复真多,插句嘴:我认为短而直观的实现总是更为优雅。Python中提供了这个功能,如果你认为用它实现最简单,就不要怀疑地去使用它。

    例如我在YUI里就用了getattr:
    https://bitbucket.org/keakon/yui/src/4adaf38a3581/yui.py#cl-1272

    web.py也用了这种方法:
    https://github.com/webpy/webpy/blob/master/web/application.py#L633

    否则得像webapp一样写很多if..else:
    http://code.google.com/p/googleappengine/source/browse/trunk/python/google/appengine/ext/webapp/__init__.py?r=142#514

    metaclass和decorator我一般只用来隐藏实现,让使用者无需关注其内部的实现。而现在的问题是你开放的这些接口本来就是用来调用的,不应该由被调用者强行注入调用者。
    这也是我觉得用decorator来标记一个handler的不妥之处:传统的url mapping方式你可以很一目了然地知道哪些url映射到哪个handler了;而像Uliweb那样写个@expose的话,你得找到所有的handler才能确定这些映射关系,并且还得关注handler的定义顺序和import的顺序。
    而这一切并没有带给你任何好处:逻辑更复杂了,阅读更困难了,维护更吃力了。


    最后说下那个help的例子,你不觉得使用decorator的实现导致help函数与其他函数之间的耦合度增加了么?
    明明其他函数已经开放了__doc__这个属性了,却还要强迫它们维护help函数所需的_actions列表。

    如果定义一个Helper类,遍历自身的方法,用getattr取它们的__doc__,就可以少写一个函数、一个列表和多处@action。并且,如果这个类被继承了的话,扩展它的人也不需要写@action来保证help()仍能正常工作。假如你哪天觉得action这个名字不好,需要重命名时,也不需要各处去查找替换@action

    当然,这个例子如果用metaclass实现的话耦合度没有这么高。但在继承时如果不想展示子类的某几个方法,却发现子类的metaclass必须继承父类的metaclass;而如果有多重继承和多个元类则会更复杂。
    虽然这种情况很极端,但你不觉得元类会把实现变得更加抽象么?回过头来想想,它本来不是个很简单的反射就直截了当能完成的事么?
    chuangbo
        20
    chuangbo  
       2011-01-12 10:54:23 +08:00
    @keakon 说的不错。简洁而优雅的实现是Pythoner喜闻乐见的。
    ssword
        21
    ssword  
       2011-01-12 17:05:05 +08:00
    @ayanamist 见笑了,我也是python新手,只是对几个概念有耳闻而已。

    楼主的情景该是猜到一些。是不是这样呢:在一个类里放很多函数,留一个函数作对外的接口,根据输入的不同分派一个不同的函数。回去想了下,使用反射也未尝不是一个好方法。要扩展,只需要往类里添加一个函数即可,比起一堆if else自是灵活的多了。

    但是,这个类是不会变得很大呢。再者,用户输入那个“动态函数”名字要是Object的函数怎么办?虽不是什么大问题,估计也是会抛异常的。

    前面说到lambda有点不准确,个人比较倾向于这样理解,即把可作为值传递的函数即lambda,或者说“匿名函数”。比如python允许这样

    def func1(x):
    return x+1

    f=func1
    f(1)

    函数是一等公民,这一来就可以方便的传递行为。而原先的类可以简单的重构一下,只保留几个简单的接口:

    class Dispatcher:
    def __init__(self):
    self.actions = {}

    def bind(self, cmd, func):
    self.action[cmd] = func

    def exec(self, cmd, *args):
    f = self.action[cmd]
    if f: f(*args)

    这一来就可以把那些函数挪到类的外面,以后要扩展的话,只要额外import一个文件即可。

    d = Dispacher()

    def hello():
    # do something

    d.bind("hello", hello)

    def sayya():
    # do something

    d.bind("sayya", sayya)

    ps: 其实Decorator也是python的一个函数式特征, @chuangbo 同学的方法很pythonic,也很喜欢 :)
    ayanamist
        22
    ayanamist  
    OP
       2011-01-12 17:11:46 +08:00
    @ssword @chuangbo 我还是公开一下我的情况吧。因为想了想,觉得如果脱开类,实现反而比较麻烦。
    https://github.com/gh05tw01f/tweet-talk/blob/master/xmpp.py#L58
    欢迎各种拍砖
    ayanamist
        23
    ayanamist  
    OP
       2011-01-12 17:14:44 +08:00
    chuangbo
        24
    chuangbo  
       2011-01-12 21:40:36 +08:00
    ayanamist
        25
    ayanamist  
    OP
       2011-01-13 14:10:36 +08:00
    @chuangbo 你是指把OAuth Consumer开放了,很多都这样做啊,我觉得这个没什么。这个东西我又不打算去申请白名单,用自己的和用我的都是一样的。除非我打算去申请白名单2W次,那就很重要了。而且我并不打算像TwiTalker那样搭建很多节点供人使用,打算问问kavin,如果kavin不打算用我的代码,那我就只用一个节点,想用的人自己部署吧,弄成像Dabr那样的情况。
    david_lee_cn
        26
    david_lee_cn  
       2011-01-16 23:33:43 +08:00
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3152 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 13:52 · PVG 21:52 · LAX 05:52 · JFK 08:52
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.