我有一个由外部库提供给我的类。我创建了这个类的一个子类。我也有一个原始类的实例。
我现在想将此实例转换为我的子类的一个实例,而不更改该实例已有的任何属性(我的子类将覆盖的属性除外)。
以下解决方案似乎有效。
# This class comes from an external library. I don't (want) to control
# it, and I want to be open to changes that get made to the class
# by the library provider.
class Programmer(object):
def __init__(self,name):
self._name = name
def greet(self):
print "Hi, my name is %s." % self._name
def hard_work(self):
print "The garbage collector will take care of everything."
# This is my subclass.
class C_Programmer(Programmer):
def __init__(self, *args, **kwargs):
super(C_Programmer,self).__init__(*args, **kwargs)
self.learn_C()
def learn_C(self):
self._knowledge = ["malloc","free","pointer arithmetic","curly braces"]
def hard_work(self):
print "I'll have to remember " + " and ".join(self._knowledge) + "."
# The questionable thing: Reclassing a programmer.
@classmethod
def teach_C(cls, programmer):
programmer.__class__ = cls # <-- do I really want to do this?
programmer.learn_C()
joel = C_Programmer("Joel")
joel.greet()
joel.hard_work()
#>Hi, my name is Joel.
#>I'll have to remember malloc and free and pointer arithmetic and curly braces.
jeff = Programmer("Jeff")
# We (or someone else) makes changes to the instance. The reclassing shouldn't
# overwrite these.
jeff._name = "Jeff A"
jeff.greet()
jeff.hard_work()
#>Hi, my name is Jeff A.
#>The garbage collector will take care of everything.
# Let magic happen.
C_Programmer.teach_C(jeff)
jeff.greet()
jeff.hard_work()
#>Hi, my name is Jeff A.
#>I'll have to remember malloc and free and pointer arithmetic and curly braces.
但是,我不相信这个解决方案不包含任何我没有想到的警告(对不起,三重否定),特别是因为重新分配神奇的 __class__ 只是不感觉对。即使这样可行,我也情不自禁地觉得应该有一种更 Python 的方式来做到这一点。
有吗?
编辑:感谢大家的回答。这是我从他们那里得到的:
虽然通过分配给 __class__ 来重新分类实例的想法不是一个广泛使用的习惯用法,但大多数答案(在撰写本文时为 6 个中的 4 个)认为它是一种有效的方法。一个答案(来自 ojrac)说它“乍一看很奇怪”,我同意(这是提出这个问题的原因)。只有一个答案(由 Jason Baker 提出;有两个正面评论和投票)强烈劝阻我不要这样做,但是这样做是基于示例用例而不是一般技术。
没有一个答案,无论是否正面,都发现此方法存在实际技术问题。一个小异常(exception)是 jls,他提到要提防旧式类(这很可能是真的)和 C 扩展。我认为这种新风格的类感知 C 扩展应该和 Python 本身一样好(假设后者是正确的),但如果你不同意,请继续给出答案。
关于这有多像pythonic的问题,有一些肯定的答案,但没有给出真正的理由。看看 Zen (import this),我想在这种情况下最重要的规则是“显式优于隐式”。不过,我不确定这条规则是支持还是反对以这种方式重新分类。
使用 {has,get,set}attr 似乎更明确,因为我们明确地对对象进行更改而不是使用魔法。
使用 __class__ = newclass 似乎更明确,因为我们明确地说“这现在是类 'newclass' 的对象,期待不同的行为”,而不是默默地改变属性,但让用户对象认为他们正在处理旧类的常规对象。
总结:从技术角度来看,方法似乎还可以; pythonicity问题仍然没有答案,偏向于"is"。
我接受了 Martin Geisler 的回答,因为 Mercurial 插件示例非常强大(也因为它回答了一个我什至还没有问过自己的问题)。但是,如果对 pythonicity 问题有任何争论,我仍然想听听。到目前为止,谢谢。
附:实际用例是一个 UI 数据控件对象,在运行时需要增加额外的功能。但是,这个问题是非常笼统的。
最佳答案
像这样的重新分类实例在 Mercurial 中完成。 (分布式修订控制系统)当扩展(插件)想要更改代表本地存储库的对象时。该对象称为repo,最初是一个localrepo 实例。它依次传递给每个扩展,并且在需要时,扩展将定义一个新类,它是 repo.__class__ 的子类和 change repo 的类 到这个新的子类!
看起来 like this在代码中:
def reposetup(ui, repo):
# ...
class bookmark_repo(repo.__class__):
def rollback(self):
if os.path.exists(self.join('undo.bookmarks')):
util.rename(self.join('undo.bookmarks'), self.join('bookmarks'))
return super(bookmark_repo, self).rollback()
# ...
repo.__class__ = bookmark_repo
扩展(我从书签扩展中获取代码)定义了一个名为 reposetup 的模块级函数。 Mercurial 将在初始化扩展时调用它并传递 ui(用户界面)和 repo(存储库)参数。
然后,该函数定义了 repo 恰好是任何类的子类。 不简单地继承 localrepo 因为扩展需要能够相互扩展。因此,如果第一个扩展将 repo.__class__ 更改为 foo_repo,则下一个扩展应该将 repo.__class__ 更改为 foo_repo<> 而不仅仅是 localrepo 的子类。最后,该函数更改了 instanceø 的类,就像您在代码中所做的那样。
我希望这段代码可以证明该语言功能的合法使用。我想这是我见过它在野外使用的唯一地方。
关于subclass - 在 Python 中重新分类实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/990758/
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende
我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击
在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg
我正在使用active_admin,我在Rails3应用程序的应用程序中有一个目录管理,其中包含模型和页面的声明。时不时地我也有一个类,当那个类有一个常量时,就像这样:classFooBAR="bar"end然后,我在每个必须在我的Rails应用程序中重新加载一些代码的请求中收到此警告:/Users/pupeno/helloworld/app/admin/billing.rb:12:warning:alreadyinitializedconstantBAR知道发生了什么以及如何避免这些警告吗? 最佳答案 在纯Ruby中:classA
我有一个正在构建的应用程序,我需要一个模型来创建另一个模型的实例。我希望每辆车都有4个轮胎。汽车模型classCar轮胎模型classTire但是,在make_tires内部有一个错误,如果我为Tire尝试它,则没有用于创建或新建的activerecord方法。当我检查轮胎时,它没有这些方法。我该如何补救?错误是这样的:未定义的方法'create'forActiveRecord::AttributeMethods::Serialization::Tire::Module我测试了两个环境:测试和开发,它们都因相同的错误而失败。 最佳答案
我正在处理旧代码的一部分。beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)endRubocop错误如下:Avoidstubbingusing'allow_any_instance_of'我读到了RuboCop::RSpec:AnyInstance我试着像下面那样改变它。由此beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)end对此:let(:sport_
我早就知道Ruby中的“常量”(即大写的变量名)不是真正常量。与其他编程语言一样,对对象的引用是唯一存储在变量/常量中的东西。(侧边栏:Ruby确实具有“卡住”引用对象不被修改的功能,据我所知,许多其他语言都没有提供这种功能。)所以这是我的问题:当您将一个值重新分配给常量时,您会收到如下警告:>>FOO='bar'=>"bar">>FOO='baz'(irb):2:warning:alreadyinitializedconstantFOO=>"baz"有没有办法强制Ruby抛出异常而不是打印警告?很难弄清楚为什么有时会发生重新分配。 最佳答案
我收到格式为的回复#我需要将其转换为哈希值(针对活跃商家)。目前我正在遍历变量并执行此操作:response.instance_variables.eachdo|r|my_hash.merge!(r.to_s.delete("@").intern=>response.instance_eval(r.to_s.delete("@")))end这有效,它将生成{:first="charlie",:last=>"kelly"},但它似乎有点hacky和不稳定。有更好的方法吗?编辑:我刚刚意识到我可以使用instance_variable_get作为该等式的第二部分,但这仍然是主要问题。
这个问题在这里已经有了答案:关闭10年前。PossibleDuplicate:Pythonconditionalassignmentoperator对于这样一个简单的问题表示歉意,但是谷歌搜索||=并不是很有帮助;)Python中是否有与Ruby和Perl中的||=语句等效的语句?例如:foo="hey"foo||="what"#assignfooifit'sundefined#fooisstill"hey"bar||="yeah"#baris"yeah"另外,类似这样的东西的通用术语是什么?条件分配是我的第一个猜测,但Wikipediapage跟我想的不太一样。