写代码不能太简单了 - V2EX
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 6114 次点击
    这是一个创建于 5387 天前的主题,其中的信息可能已经有所发展或是发生改变。
    项目中用了反射,直接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     Solana     5327 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 22ms UTC 09:15 PVG 17:15 LAX 02:15 JFK 05:15
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86