在一些个人项目中,我正在慢慢地从PHP5转向Python,我现在很喜欢这种体验.在选择沿着Python路线前,我看了Ruby.我从红宝石社区注意到的是,猴子修补既常见又备受推崇.我也遇到了很多关于调试ruby s/w的试验的恐怖故事,因为有人包括一个相对无害的库来完成一些工作但是修补了一些使用频繁的核心对象而没有告诉任何人.
我选择Python(除了其他原因)之外它的语法更清晰,而且它可以完成Ruby所能做的一切.Python正在使得OO点击比PHP更好,我正在越来越多地阅读OO原则以增强这种更好的理解.
今晚我一直在阅读Robert Martin的SOLID原则:
我目前正在接受O:软件实体(课程,模块,功能等)应该开放扩展,但是为了修改而关闭.
我的头脑是在确保OO设计的一致性和整个猴子修补之间的冲突.我知道可以在Python中进行猴子修补.我也明白,"pythonic"是遵循常见的,经过良好测试的oop最佳实践和原则.
我想知道的是社区对两个对立主题的看法; 他们如何互操作,当它最好地使用一个在另一个上时,是否应该完成猴子修补......希望你能为我提供解决问题的方法.
我想使用Celery作为我的任务的队列,所以我的网络应用程序可以排队任务,返回响应,同时/某天/任务将处理任务...我建立了一种API,所以我不知道将提供什么样的任务 - 将来,可以有处理HTTP请求的任务,另一个IO,以及CPU消耗任务.关于这一点,我想在进程上运行Celery的工作者,因为这些是Python中的通用类型的并行性.
但是,我也想在我的任务中使用gevent,所以我可以有一个任务产生许多HTTP请求,等等.问题是,当我这样做时:
from gevent import monkey
monkey.patch_all()
Run Code Online (Sandbox Code Playgroud)
芹菜停止工作.它开始,但没有任何任务可以有效排队 - 他们似乎去经纪人,但芹菜工人不收集它们并处理它们.只有开始和等待.如果我删除这些行并执行任务而没有任何gevent和并行化,一切正常.
我想这可能是因为gevent补丁也在线程化.所以我试过了
from gevent import monkey
monkey.patch_all(thread=False)
Run Code Online (Sandbox Code Playgroud)
...但是Celery甚至没有启动,它在没有给出原因的情况下崩溃(启用了日志记录的调试级别).
是否有可能使用Celery来排队任务和gevent在一个任务中做一些事情?怎么样?我做错了什么?
我正在试图修补(duck-punch :-)一个LWP::UserAgent实例,如下所示:
sub _user_agent_get_basic_credentials_patch {
return ($username, $password);
}
my $agent = LWP::UserAgent->new();
$agent->get_basic_credentials = _user_agent_get_basic_credentials_patch;
Run Code Online (Sandbox Code Playgroud)
这不是正确的语法 - 它产生:
无法在[module] line [lineno]修改非左值子程序调用.
我记得(来自Programming Perl),调度查找是基于受祝福的包动态执行的(ref($agent)我相信),所以我不确定实例猴子修补如何在不影响受祝福的包的情况下工作.
我知道我可以继承它UserAgent,但我更喜欢更简洁的猴子修补方法.同意成年人和你有什么.;-)
我不想讨论这种方法的优点,只要有可能.我相信答案是"不".但也许有人会让我感到惊讶!
想象一下,你有一个核心小部件类.它有一个calculateHeight()返回高度的方法.高度太大 - 这导致按钮(比如说)太大了.您可以扩展DefaultWidget以创建自己的NiceWidget,并实现自己calculateHeight()的返回更好的大小.
现在,一个库类WindowDisplayFactory,在一个相当复杂的方法中实例化DefaultWidget.您希望它使用您的NiceWidget.工厂类的方法看起来像这样:
public IWidget createView(Component parent) {
DefaultWidget widget = new DefaultWidget(CONSTS.BLUE, CONSTS.SIZE_STUPIDLY);
// bunch of ifs ...
SomeOtherWidget bla = new SomeOtherWidget(widget);
SomeResultWidget result = new SomeResultWidget(parent);
SomeListener listener = new SomeListener(parent, widget, flags);
// more widget creation and voodoo here
return result;
}
Run Code Online (Sandbox Code Playgroud)
这就是交易.结果使DefaultWidget深入其他对象的层次结构中.问题 - 如何让这个工厂方法使用我自己的NiceWidget?或者至少calculateHeight()在那里自己动手.理想情况下,我希望能够修补DefaultWidget,以便其calculateHeight做正确的事情......
public class MyWindowDisplayFactory {
public IWidget createView(Component parent) {
DefaultWidget.class.setMethod("calculateHeight", myCalculateHeight);
return super.createView(parent);
}
}
Run Code Online (Sandbox Code Playgroud)
这是我在Python,Ruby等方面可以做的事情setMethod().虽然我已经发明了这个名字.对我开放的其他选择是:
createView()方法的代码复制并粘贴到我自己的继承工厂类的类中工厂类无法更改 …
我在我的Rails项目中使用Devise身份验证gem,我想更改它在闪存警报中使用的密钥.(设计使用:通知和:警告闪存键,但我想将它们更改为:成功和:错误,以便我可以使用Bootstrap显示漂亮的绿色/红色框.)
所以我希望能够以某种方式覆盖DeviseController中的set_flash_message方法.
这是新方法:
def set_flash_message(key, kind, options = {})
if key == 'alert'
key = 'error'
elsif key == 'notice'
key = 'success'
end
message = find_message(kind, options)
flash[key] = message if message.present?
end
Run Code Online (Sandbox Code Playgroud)
但我只是不知道该把它放在哪里.
更新:
基于答案,我使用以下代码创建了config/initializers/overrides.rb文件:
class DeviseController
def set_flash_message(key, kind, options = {})
if key == 'alert'
key = 'error'
elsif key == 'notice'
key = 'success'
end
message = find_message(kind, options)
flash[key] = message if message.present?
end
end
Run Code Online (Sandbox Code Playgroud)
但这会导致每个Devise操作出错: …
像Ruby和JavaScript这样的语言有开放类,允许你修改偶数核心类的接口,如数字,字符串,数组等.显然,这样做可能会使熟悉API的其他人感到困惑,但是有充分的理由要避免使用它们. ,假设您正在添加到界面而不是更改现有行为?
例如,将一个Array.map实现添加到未实现ECMAScript第5版的Web浏览器(如果您不需要所有jQuery)可能会很好.或者你的Ruby数组可能会受益于使用"inject"的"sum"方便方法.只要更改被隔离到您的系统(例如,不是您发布用于分发的软件包的一部分),是否有充分的理由不利用此语言功能?
MethodType从types模块使用有什么好处?您可以使用它向对象添加方法.但是如果没有它我们可以很容易地做到:
def func():
print 1
class A:
pass
obj = A()
obj.func = func
Run Code Online (Sandbox Code Playgroud)
即使我们func通过运行在主范围中删除它也可以工作del func.
为什么要使用MethodType?这只是一个约定还是一个好的编程习惯?
在我的Django环境"完全加载"之后,我需要执行一些相当简单的任务.
更具体地说,我需要做一些像Signal.disconnect()我的第三方库默认设置的Django Signals和connect我自己的Signals,我需要做一些"猴子修补",以便从另一个库中为一些Django模型添加便利功能.
我一直在我的Django应用程序的__init__.py文件中做这些东西,这似乎适用于猴子修补,但不适用于我的信号断开连接.问题似乎是时间问题 - 无论出于什么原因,第三方图书馆Signal.connect()在我尝试之后似乎总是称之为Signal.disconnect()它.
所以有两个问题:
根据INSTALLED_APPS我的应用程序__init__.py模块加载时的顺序,我有任何保证吗?
在 Django应用程序完全加载到内存后,是否有适当的位置来放置需要运行的逻辑?
我在这里和那里看到一些关于修改JavaScript对象原型的不满意的评论?我个人不知道这可能是一个什么问题.例如,扩展Array对象以具有map和include方法或创建更强大的Date方法?
我想修补一个例程调用,以便能够通过一些修改自己处理它.我正在写一个资源加载器.我想修补Delphi的LoadResourceModule和InitInheritedComponent例程.我已经在MadExcept.pas单元中检查了PatchAPI调用,但如果我可以将其用于我的项目,则无法弄明白.
我想要类似的东西
我的exe在运行时调用 - > LoadResourceModule - >跳转到 - > MyCustomResourceModule ...
任何关于此的指针都会非常有帮助.
monkeypatching ×10
python ×3
ruby ×3
javascript ×2
oop ×2
celery ×1
delphi ×1
devise ×1
django ×1
gevent ×1
java ×1
methods ×1
perl ×1
reflection ×1
rubygems ×1