写代码不能太简单了

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

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

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

不要太依赖异常,或者在抛出的时候塞进多些信息。
ayanamist
2011-01-11 23:28:37 +08:00
@ssword 求指教,我可以把完整代码给你看。这是未来的开源项目,不过现在太不稳定,经常重构,所以暂时不适合公开。已加Gtalk
ayanamist
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
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
2011-01-12 00:24:14 +08:00
@chuangbo 多谢~今天头脑爆炸了,学到很多东西,由上面@ssword同学找到了Y Combinator的东西,你这里又有meta class,学不完了……TAT
chuangbo
2011-01-12 00:41:12 +08:00
或者更简单的,可以用decorator
写了个简单的例子,十分好理解,用来提示help的命令的:
http://gist.github.com/774683
ayanamist
2011-01-12 00:48:05 +08:00
@chuangbo 谢谢~今天(12号)晚上我再研究研究我的代码,不过我想问问的是,用反射会不会更好一些呢?
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
2011-01-12 10:54:23 +08:00
@keakon 说的不错。简洁而优雅的实现是Pythoner喜闻乐见的。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/7032

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX