- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我正在使用一个包,该包为我提供了一个对象,其中填充了一堆数据,我不想费心手动序列化这些数据并用于初始化另一个对象。我想要做的是为我自己的目的附加一堆额外的方法到对象上。
理想情况下,我想神奇地子类化一个实例,但这似乎不可能。 Monkey-patching 可能会“起作用”,但互联网上说它不是很好的形式,而且因为我代码的其他部分实际上可能在其他地方使用 native 类,所以看起来很危险。
我尝试创建一个包装器对象,但是很多(全部?)the magic methods (e.g. __iter__
) skip the __getattribute__
call ,所以不完整。打一堆传递函数定义(例如 def __iter__(self): return iter(object.__getattribute__(self, '_digraph'))
)看起来很笨重(我可能会忘记一个) .
class ColliderGraph(object):
def __init__(self, digraph):
self._digraph = digraph
def __getattribute__(self, name):
cls_attrs = ['_digraph', 'whereis', 'copy'] # more to follow.
if name not in cls_attrs:
return object.__getattribute__(
object.__getattribute__(self, '_digraph'), name)
else:
return object.__getattribute__(self, name)
#return getattr(self._digraph, name)
def whereis(self, node):
"""find a node inside other nodes of the digraph"""
def copy(self):
return ColliderGraph(self._digraph.copy())
我在其他地方以更有限的方式开始 patch the instance像这样的单个奇数函数:
def _whereis_addon(digraph, node):
"""For patching onto specially-modifed digraph objects."""
# then elsewhere...
digraph.whereis = types.MethodType(_whereis_addon, digraph)
但是如果 .copy()
被调用然后它失去升级(我想我也可以修补它......),并且以这种方式添加一堆方法也看起来很丑陋,但也许可行。
有没有更好的出路?
最佳答案
首先,我认为最明智的选择是修补 digraph
实例以添加您需要的方法,并在其中修补 __copy__
,或者甚至坚持使用您的包装器,并使用元类为魔术方法添加代理,如 this answer 中所建议到您链接到的问题。
就是说,我最近在考虑“神奇地”子类化一个实例的想法,并认为我会与您分享我的发现,因为您正在考虑同样的事情。这是我想出的代码:
def retype_instance(recvinst, sendtype, metaklass=type):
""" Turn recvinst into an instance of sendtype.
Given an instance (recvinst) of some class, turn that instance
into an instance of class `sendtype`, which inherits from
type(recvinst). The output instance will still retain all
the instance methods and attributes it started with, however.
For example:
Input:
type(recvinst) == Connection
sendtype == AioConnection
metaklass == CoroBuilder (metaclass used for creating AioConnection)
Output:
recvinst.__class__ == AioConnection
recvinst.__bases__ == bases_of_AioConnection +
Connection + bases_of_Connection
"""
# Bases of our new instance's class should be all the current
# bases, all of sendtype's bases, and the current type of the
# instance. The set->tuple conversion is done to remove duplicates
# (this is required for Python 3.x).
bases = tuple(set((type(recvinst),) + type(recvinst).__bases__ +
sendtype.__bases__))
# We change __class__ on the instance to a new type,
# which should match sendtype in every where, except it adds
# the bases of recvinst (and type(recvinst)) to its bases.
recvinst.__class__ = metaklass(sendtype.__name__, bases, {})
# This doesn't work because of http://bugs.python.org/issue672115
#sendtype.__bases__ = bases
#recv_inst.__class__ = sendtype
# Now copy the dict of sendtype to the new type.
dct = sendtype.__dict__
for objname in dct:
if not objname.startswith('__'):
setattr(type(recvinst), objname, dct[objname])
return recvinst
思路是重新定义实例的__class__
,将其变为我们选择的新类,并将__class__
的原始值添加到 inst.__bases__
(连同新类型的 __bases__
)。此外,我们将新类型的 __dict__
复制到实例中。这听起来相当疯狂而且可能确实如此,但在我对它进行的少量测试中,它似乎(大部分)确实有效:
class MagicThread(object):
def magic_method(self):
print("This method is magic")
t = Thread()
m = retype_instance(t, MagicThread)
print m.__class__
print type(m)
print type(m).__mro__
print isinstance(m, Thread)
print dir(m)
m.magic_method()
print t.is_alive()
print t.name
print isinstance(m, MagicThread)
输出:
<class '__main__.MagicThread'>
<class '__main__.MagicThread'>
(<class '__main__.MagicThread'>, <class 'threading.Thread'>, <class 'threading._Verbose'>, <type 'object'>)
True
['_Thread__args', '_Thread__block', '_Thread__bootstrap', '_Thread__bootstrap_inner', '_Thread__daemonic', '_Thread__delete', '_Thread__exc_clear', '_Thread__exc_info', '_Thread__ident', '_Thread__initialized', '_Thread__kwargs', '_Thread__name', '_Thread__started', '_Thread__stderr', '_Thread__stop', '_Thread__stopped', '_Thread__target', '_Verbose__verbose', '__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_block', '_note', '_reset_internal_locks', '_set_daemon', '_set_ident', 'daemon', 'getName', 'ident', 'isAlive', 'isDaemon', 'is_alive', 'join', 'magic_method', 'name', 'run', 'setDaemon', 'setName', 'start']
This method is magic
False
Thread-1
False
除了最后一行 - isinstance(m, MagicThread)
是 False
之外,所有输出都完全符合我们的要求。这是因为我们实际上并没有将 __class__
分配给我们定义的 MagicMethod
类。相反,我们创建了一个具有相同名称和所有相同方法/属性的单独类。理想情况下,这可以通过在 retype_instance
中实际重新定义 MagicThread
的 __bases__
来解决,但 Python 不允许这样做:
TypeError: __bases__ assignment: 'Thread' deallocator differs from 'object'
这似乎是一个 bug in Python一直追溯到 2003 年。它还没有被修复,可能是因为在实例上动态重新定义 __bases__
是一个奇怪的而且可能是个坏主意!
现在,如果您不关心能否使用 isinstance(obj, ColliderGraph)
,以上内容可能适合您。或者它可能会以奇怪的、意想不到的方式失败。我真的不建议在任何生产代码中使用它,但我想我会分享它。
关于python - 向现有类实例添加方法,或如何将实例添加到 "subclass",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25394550/
有没有一种方法可以使用标准类型构造函数(例如 int、set、dict、list、tuple 等)以用户定义的方式将用户定义类的实例强制转换为其中一种类型?例如 class Example:
我知道这个问题在Stackoverflow中有很多问题,但是即使有很多答案,这些答案也帮不了我什么,也没有找到答案。 在我的WebAPP中,它可以正常工作,但是当我将其转换为API时,它失败了(主题标
这个问题已经有答案了: Why does the ternary operator unexpectedly cast integers? (3 个回答) 已关闭 9 年前。 最近遇到一个Java的陷
我尝试使用 FirebaseApp.configure() 配置 Firebase,但遇到以下崩溃: *** Terminating app due to uncaught exception 'c
我有一个自连接员工实体类,其中包含与其自身相关的 id、name 和 ref 列。我想创建它的新实例并将其保存到数据库。 首先我创建了一个 Employee 类的实例并将其命名为 manager。然后
我有一个用于添加新公寓的表单,在该表单中我有一个下拉列表,用户可以在其中选择负责的人员。 显然,当您从下拉列表中选择并尝试保存公寓时,我的应用程序认为该人已被修改。它给了我下面的错误,指示我应该首先保
从 Visualforce 页面,我需要检索我们组织的 salesforce 实例的 URL,而不是 Visual Force URL。 例如我需要https://cs1.salesforce.com
我遇到了一些可能的问题答案,但这是关于从 Hibernate 3.4.0GA 升级到 Hibernate 4.1.8 的问题。所以这曾经在以前的版本下工作,我已经四处搜索了为什么它在这个新版本中出现了
似乎一遍又一遍地问这个问题,我仍然找不到解决我问题的答案。我在下面有一个域模型。每个新创建或更新的“安全用户”都需要我确保其具有配置文件,如果没有,则创建一个新的配置文件并分配给它。 配置文件的要求相
我很难调试为什么 JPA 不级联我的 @ManyToMany 关系。我发现的所有答案都与缺少级联语句有关。但我确实拥有它们并且仍然得到: Caused by: org.hibernate.Transi
Play 服务 API 表明有一个叫做 Instance ID 的东西 但是,在 Android Studio 中包含以下内容后,我无法导入 InstanceID 类 compile "com.goo
我正在使用 Seam 框架。我有 2 个实体: 请求.java @Entity @Table(name = "SRV_REQUEST") public class Request { private
This question处理构建一个适当的Monad来自单子(monad)的实例,但仅在某些约束下 - 例如Set .诀窍是将其包装成 ContT ,它将约束推迟到包装/展开其值。 现在我想对 Ap
我正在尝试执行此查询: StringBuffer sb = new StringBuffer(); sb.append("select p from PointsEntity p " + "where
我试图了解是否可以更改我的 hibernate 配置并使用单个 MySQL 实例(而不是我当前拥有的多个 MySQL 实例): 我有一个使用 hibernate 的 Java 应用程序,与 2 个模式
我有一个选项卡滑动布局,其中包括四个选项卡,每个选项卡都有自己的布局和 fragment ,在我的主要 Activity 布局中,viewpager 参与更改选项卡。特定 View (选项卡)在应用程
我看到很多帖子声称他们正在运行 MySql 的 RDS 实例,但无法连接到该实例,但我没有运行 RDS。 我使用 EC2 实例来托管我的 WordPress 博客,该博客是使用 Web 平台安装程序安
因为我在我的 ec-2 实例上的 python 虚拟环境中运行应用程序( Airflow ),并且我想在同一个 ec2 实例上的默认 python 环境中运行命令,所以我认为 ssh 到我自己的实例更
这个问题已经有答案了: How to fix the Hibernate "object references an unsaved transient instance - save the tra
例子: run APP1 .. ... run APP1 ... run APP2 如何在 APP2 中对 Vue 说我需要调用 APP1?
我是一名优秀的程序员,十分优秀!