在 Python 中向现有对象实例添加方法

发布于 2025-01-12 08:43:21 字数 74 浏览 5 评论 0原文

如何在 Python 中向现有对象(即不在类定义中)添加方法?

据我所知,除了某些情况外,这样做通常不被认为是良好做法。

How do I add a method to an existing object (i.e., not in the class definition) in Python?

I understand that it's not generally considered good practice to do so, except in some cases.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(19

睫毛上残留的泪 2025-01-19 08:43:21

在 Python 中,函数和绑定方法之间存在差异。

>>> def foo():
...     print "foo"
...
>>> class A:
...     def bar( self ):
...         print "bar"
...
>>> a = A()
>>> foo
<function foo at 0x00A98D70>
>>> a.bar
<bound method A.bar of <__main__.A instance at 0x00A9BC88>>
>>>

绑定方法已“绑定”(如何描述)到实例,并且每当调用该方法时,该实例都将作为第一个参数传递。

不过,作为类的属性(而不是实例)的可调用对象仍然未绑定,因此您可以随时修改类定义:

>>> def fooFighters( self ):
...     print "fooFighters"
...
>>> A.fooFighters = fooFighters
>>> a2 = A()
>>> a2.fooFighters
<bound method A.fooFighters of <__main__.A instance at 0x00A9BEB8>>
>>> a2.fooFighters()
fooFighters

以前定义的实例也会更新(只要它们没有覆盖属性本身) ):

>>> a.fooFighters()
fooFighters

当您想要将方法附加到单个实例时,问题就出现了:

>>> def barFighters( self ):
...     print "barFighters"
...
>>> a.barFighters = barFighters
>>> a.barFighters()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: barFighters() takes exactly 1 argument (0 given)

当函数直接附加到实例时,不会自动绑定:

>>> a.barFighters
<function barFighters at 0x00A98EF0>

要绑定它,我们可以使用 types 模块中的 MethodType 函数

>>> import types
>>> a.barFighters = types.MethodType( barFighters, a )
>>> a.barFighters
<bound method ?.barFighters of <__main__.A instance at 0x00A9BC88>>
>>> a.barFighters()
barFighters

这次该类的其他实例有未受到影响:

>>> a2.barFighters()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: A instance has no attribute 'barFighters'

可以通过阅读有关 描述符元类 编程

In Python, there is a difference between functions and bound methods.

>>> def foo():
...     print "foo"
...
>>> class A:
...     def bar( self ):
...         print "bar"
...
>>> a = A()
>>> foo
<function foo at 0x00A98D70>
>>> a.bar
<bound method A.bar of <__main__.A instance at 0x00A9BC88>>
>>>

Bound methods have been "bound" (how descriptive) to an instance, and that instance will be passed as the first argument whenever the method is called.

Callables that are attributes of a class (as opposed to an instance) are still unbound, though, so you can modify the class definition whenever you want:

>>> def fooFighters( self ):
...     print "fooFighters"
...
>>> A.fooFighters = fooFighters
>>> a2 = A()
>>> a2.fooFighters
<bound method A.fooFighters of <__main__.A instance at 0x00A9BEB8>>
>>> a2.fooFighters()
fooFighters

Previously defined instances are updated as well (as long as they haven't overridden the attribute themselves):

>>> a.fooFighters()
fooFighters

The problem comes when you want to attach a method to a single instance:

>>> def barFighters( self ):
...     print "barFighters"
...
>>> a.barFighters = barFighters
>>> a.barFighters()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: barFighters() takes exactly 1 argument (0 given)

The function is not automatically bound when it's attached directly to an instance:

>>> a.barFighters
<function barFighters at 0x00A98EF0>

To bind it, we can use the MethodType function in the types module:

>>> import types
>>> a.barFighters = types.MethodType( barFighters, a )
>>> a.barFighters
<bound method ?.barFighters of <__main__.A instance at 0x00A9BC88>>
>>> a.barFighters()
barFighters

This time other instances of the class have not been affected:

>>> a2.barFighters()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: A instance has no attribute 'barFighters'

More information can be found by reading about descriptors and metaclass programming.

灼疼热情 2025-01-19 08:43:21

前言 - 关于兼容性的说明:其他答案可能只适用于 Python 2 - 这个答案应该在 Python 2 和 3 中工作得很好。如果只编写 Python 3,您可能会省略显式继承 object,但除此之外,代码应保持不变。

向现有对象实例添加方法

我读到可以在 Python 中向现有对象(例如不在类定义中)添加方法。

我知道这样做并不总是一个好的决定。 但是,如何才能做到这一点呢?

是的,这是可能的 - 但不推荐

我不推荐这样做。这是一个坏主意。不要这样做。

原因如下:

  • 您将向执行此操作的每个实例添加一个绑定对象。如果你经常这样做,你可能会浪费很多内存。绑定方法通常仅在其调用的短时间内创建,然后在自动垃圾收集时它们将不再存在。如果您手动执行此操作,您将拥有一个引用绑定方法的名称绑定 - 这将阻止其在使用时进行垃圾收集。
  • 给定类型的对象实例通常对该类型的所有对象都有其方法。如果您在其他地方添加方法,某些实例将具有这些方法,而其他实例则不会。程序员不会预料到这一点,并且您可能会违反最小惊喜规则
  • 由于还有其他充分的理由不这样做,因此如果您这样做,您还会给自己带来不好的声誉。

因此,我建议您不要这样做,除非您有充分的理由。 最好在类定义中定义正确的方法,或者更少最好直接对类进行猴子修补,如下所示:

Foo.sample_method = sample_method

因为它很有启发性,但是,我将向您展示一些执行此操作的方法。

如何做到

这里有一些设置代码。我们需要一个类定义。可以进口,但没关系。

class Foo(object):
    '''An empty class to demonstrate adding a method to an instance'''

创建一个实例:

foo = Foo()

创建一个方法来添加到它:

def sample_method(self, bar, baz):
    print(bar + baz)

Method naught (0) - 使用描述符方法,__get__

函数上的点式查找调用该函数的 __get__ 方法实例,将对象绑定到方法,从而创建“绑定方法”。

foo.sample_method = sample_method.__get__(foo)

现在:

>>> foo.sample_method(1,2)
3

方法一 - types.MethodType

首先,导入类型,从中我们将获得方法构造函数:

import types

现在我们将方法添加到实例中。为此,我们需要来自 types 模块(我们在上面导入的)的 MethodType 构造函数。

types.MethodType 的参数签名(在 Python 3 中)是 (function, instance):

foo.sample_method = types.MethodType(sample_method, foo)

用法:

>>> foo.sample_method(1,2)
3

顺便说一句,在 Python 2 中签名是 (function, instance, class) >:

foo.sample_method = types.MethodType(sample_method, foo, Foo)

方法二:词法绑定

首先,我们创建一个包装函数,将方法绑定到实例:

def bind(instance, method):
    def binding_scope_fn(*args, **kwargs): 
        return method(instance, *args, **kwargs)
    return binding_scope_fn

用法:

>>> foo.sample_method = bind(foo, sample_method)    
>>> foo.sample_method(1,2)
3

方法三:functools.partial

部分函数将第一个参数应用于函数(以及可选的关键字参数),稍后可以调用与其余参数(以及覆盖关键字参数)。因此:

>>> from functools import partial
>>> foo.sample_method = partial(sample_method, foo)
>>> foo.sample_method(1,2)
3    

当您认为绑定方法是实例的部分函数时,这是有道理的。

未绑定函数作为对象属性 - 为什么这不起作用:

如果我们尝试以与将其添加到类中相同的方式添加sample_method,它将与实例解除绑定,并且不会将隐式 self 作为第一个论点。

>>> foo.sample_method = sample_method
>>> foo.sample_method(1,2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: sample_method() takes exactly 3 arguments (2 given)

我们可以通过显式传递实例(或任何东西,因为此方法实际上并不使用 self 参数变量)来使未绑定函数工作,但它与其他实例的预期签名不一致(如果我们要对这个实例进行猴子修补):

>>> foo.sample_method(foo, 1, 2)
3

结论

您现在知道可以通过多种方法来执行此操作,但严肃地说 - 不要执行此操作。

Preface - a note on compatibility: other answers may only work in Python 2 - this answer should work perfectly well in Python 2 and 3. If writing Python 3 only, you might leave out explicitly inheriting from object, but otherwise the code should remain the same.

Adding a Method to an Existing Object Instance

I've read that it is possible to add a method to an existing object (e.g. not in the class definition) in Python.

I understand that it's not always a good decision to do so. But, how might one do this?

Yes, it is possible - But not recommended

I don't recommend this. This is a bad idea. Don't do it.

Here's a couple of reasons:

  • You'll add a bound object to every instance you do this to. If you do this a lot, you'll probably waste a lot of memory. Bound methods are typically only created for the short duration of their call, and they then cease to exist when automatically garbage collected. If you do this manually, you'll have a name binding referencing the bound method - which will prevent its garbage collection on usage.
  • Object instances of a given type generally have its methods on all objects of that type. If you add methods elsewhere, some instances will have those methods and others will not. Programmers will not expect this, and you risk violating the rule of least surprise.
  • Since there are other really good reasons not to do this, you'll additionally give yourself a poor reputation if you do it.

Thus, I suggest that you not do this unless you have a really good reason. It is far better to define the correct method in the class definition or less preferably to monkey-patch the class directly, like this:

Foo.sample_method = sample_method

Since it's instructive, however, I'm going to show you some ways of doing this.

How it can be done

Here's some setup code. We need a class definition. It could be imported, but it really doesn't matter.

class Foo(object):
    '''An empty class to demonstrate adding a method to an instance'''

Create an instance:

foo = Foo()

Create a method to add to it:

def sample_method(self, bar, baz):
    print(bar + baz)

Method nought (0) - use the descriptor method, __get__

Dotted lookups on functions call the __get__ method of the function with the instance, binding the object to the method and thus creating a "bound method."

foo.sample_method = sample_method.__get__(foo)

and now:

>>> foo.sample_method(1,2)
3

Method one - types.MethodType

First, import types, from which we'll get the method constructor:

import types

Now we add the method to the instance. To do this, we require the MethodType constructor from the types module (which we imported above).

The argument signature for types.MethodType (in Python 3) is (function, instance):

foo.sample_method = types.MethodType(sample_method, foo)

and usage:

>>> foo.sample_method(1,2)
3

Parenthetically, in Python 2 the signature was (function, instance, class):

foo.sample_method = types.MethodType(sample_method, foo, Foo)

Method two: lexical binding

First, we create a wrapper function that binds the method to the instance:

def bind(instance, method):
    def binding_scope_fn(*args, **kwargs): 
        return method(instance, *args, **kwargs)
    return binding_scope_fn

usage:

>>> foo.sample_method = bind(foo, sample_method)    
>>> foo.sample_method(1,2)
3

Method three: functools.partial

A partial function applies the first argument(s) to a function (and optionally keyword arguments), and can later be called with the remaining arguments (and overriding keyword arguments). Thus:

>>> from functools import partial
>>> foo.sample_method = partial(sample_method, foo)
>>> foo.sample_method(1,2)
3    

This makes sense when you consider that bound methods are partial functions of the instance.

Unbound function as an object attribute - why this doesn't work:

If we try to add the sample_method in the same way as we might add it to the class, it is unbound from the instance, and doesn't take the implicit self as the first argument.

>>> foo.sample_method = sample_method
>>> foo.sample_method(1,2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: sample_method() takes exactly 3 arguments (2 given)

We can make the unbound function work by explicitly passing the instance (or anything, since this method doesn't actually use the self argument variable), but it would not be consistent with the expected signature of other instances (if we're monkey-patching this instance):

>>> foo.sample_method(foo, 1, 2)
3

Conclusion

You now know several ways you could do this, but in all seriousness - don't do this.

蒗幽 2025-01-19 08:43:21

模块 new 自 python 2.6 起已弃用,并在 3.0 中删除,使用 types

参见 http://docs.python.org/library/new.html

在下面的示例中,我故意从 patch_me() 函数中删除了返回值。
我认为给出返回值可能会让人相信 patch 返回一个新对象,但事实并非如此 - 它修改了传入的对象。也许这可以促进更严格地使用猴子补丁。

import types

class A(object):#but seems to work for old style objects too
    pass

def patch_me(target):
    def method(target,x):
        print "x=",x
        print "called from", target
    target.method = types.MethodType(method,target)
    #add more if needed

a = A()
print a
#out: <__main__.A object at 0x2b73ac88bfd0>  
patch_me(a)    #patch instance
a.method(5)
#out: x= 5
#out: called from <__main__.A object at 0x2b73ac88bfd0>
patch_me(A)
A.method(6)        #can patch class too
#out: x= 6
#out: called from <class '__main__.A'>

Module new is deprecated since python 2.6 and removed in 3.0, use types

see http://docs.python.org/library/new.html

In the example below I've deliberately removed return value from patch_me() function.
I think that giving return value may make one believe that patch returns a new object, which is not true - it modifies the incoming one. Probably this can facilitate a more disciplined use of monkeypatching.

import types

class A(object):#but seems to work for old style objects too
    pass

def patch_me(target):
    def method(target,x):
        print "x=",x
        print "called from", target
    target.method = types.MethodType(method,target)
    #add more if needed

a = A()
print a
#out: <__main__.A object at 0x2b73ac88bfd0>  
patch_me(a)    #patch instance
a.method(5)
#out: x= 5
#out: called from <__main__.A object at 0x2b73ac88bfd0>
patch_me(A)
A.method(6)        #can patch class too
#out: x= 6
#out: called from <class '__main__.A'>
滿滿的愛 2025-01-19 08:43:21

我觉得上面的回答都没有抓住重点。

让我们有一个带有方法的类:

class A(object):
    def m(self):
        pass

现在,让我们在 ipython 中使用它:

In [2]: A.m
Out[2]: <unbound method A.m>

好的,所以 m() 不知何故成为 A 的未绑定方法。但真的是这样吗?

In [5]: A.__dict__['m']
Out[5]: <function m at 0xa66b8b4>

事实证明,m() 只是一个函数,对其的引用被添加到 A 类字典中 - 没有什么魔法。那为什么Am给我们一个未绑定的方法呢?这是因为点没有转换为简单的字典查找。它实际上是对 A.__class__.__getattribute__(A, 'm'): 的调用:

In [11]: class MetaA(type):
   ....:     def __getattribute__(self, attr_name):
   ....:         print str(self), '-', attr_name

In [12]: class A(object):
   ....:     __metaclass__ = MetaA

In [23]: A.m
<class '__main__.A'> - m
<class '__main__.A'> - m

现在,我完全不确定为什么最后一行被打印两次,但仍然很清楚那里发生了什么。

现在,默认的 __getattribute__ 的作用是检查该属性是否是所谓的 描述符与否,即它是否实现了特殊的 __get__ 方法。如果它实现了该方法,那么返回的就是调用该 __get__ 方法的结果。回到我们的 A 类的第一个版本,这就是我们所拥有的:

In [28]: A.__dict__['m'].__get__(None, A)
Out[28]: <unbound method A.m>

并且因为 Python 函数实现了描述符协议,所以如果代表一个对象调用它们,它们会将自己绑定到该对象他们的 __get__ 方法。

好的,那么如何向现有对象添加方法呢?假设您不介意修补类,它就像这样简单:

B.m = m

然后 Bm “成为”一个未绑定的方法,这要归功于描述符的魔力。

如果您只想向单个对象添加方法,那么您必须使用 types.MethodType 自己模拟机器:

b.m = types.MethodType(m, b)

顺便说一句:

In [2]: A.m
Out[2]: <unbound method A.m>

In [59]: type(A.m)
Out[59]: <type 'instancemethod'>

In [60]: type(b.m)
Out[60]: <type 'instancemethod'>

In [61]: types.MethodType
Out[61]: <type 'instancemethod'>

I think that the above answers missed the key point.

Let's have a class with a method:

class A(object):
    def m(self):
        pass

Now, let's play with it in ipython:

In [2]: A.m
Out[2]: <unbound method A.m>

Ok, so m() somehow becomes an unbound method of A. But is it really like that?

In [5]: A.__dict__['m']
Out[5]: <function m at 0xa66b8b4>

It turns out that m() is just a function, reference to which is added to A class dictionary - there's no magic. Then why A.m gives us an unbound method? It's because the dot is not translated to a simple dictionary lookup. It's de facto a call of A.__class__.__getattribute__(A, 'm'):

In [11]: class MetaA(type):
   ....:     def __getattribute__(self, attr_name):
   ....:         print str(self), '-', attr_name

In [12]: class A(object):
   ....:     __metaclass__ = MetaA

In [23]: A.m
<class '__main__.A'> - m
<class '__main__.A'> - m

Now, I'm not sure out of the top of my head why the last line is printed twice, but still it's clear what's going on there.

Now, what the default __getattribute__ does is that it checks if the attribute is a so-called descriptor or not, i.e. if it implements a special __get__ method. If it implements that method, then what is returned is the result of calling that __get__ method. Going back to the first version of our A class, this is what we have:

In [28]: A.__dict__['m'].__get__(None, A)
Out[28]: <unbound method A.m>

And because Python functions implement the descriptor protocol, if they are called on behalf of an object, they bind themselves to that object in their __get__ method.

Ok, so how to add a method to an existing object? Assuming you don't mind patching class, it's as simple as:

B.m = m

Then B.m "becomes" an unbound method, thanks to the descriptor magic.

And if you want to add a method just to a single object, then you have to emulate the machinery yourself, by using types.MethodType:

b.m = types.MethodType(m, b)

By the way:

In [2]: A.m
Out[2]: <unbound method A.m>

In [59]: type(A.m)
Out[59]: <type 'instancemethod'>

In [60]: type(b.m)
Out[60]: <type 'instancemethod'>

In [61]: types.MethodType
Out[61]: <type 'instancemethod'>
桃扇骨 2025-01-19 08:43:21

在 Python 中,monkeypatching 通常通过用您自己的签名覆盖类或函数的签名来工作。以下是 Zope Wiki< 的示例/a>:

from SomeOtherProduct.SomeModule import SomeClass
def speak(self):
   return "ook ook eee eee eee!"
SomeClass.speak = speak

此代码将覆盖/创建类中名为 speak 的方法。在 Jeff Atwood 的最近关于猴子补丁的帖子中,他展示了 C# 3.0 中的一个示例:我当前工作使用的语言。

In Python monkeypatching generally works by overwriting a class or function's signature with your own. Below is an example from the Zope Wiki:

from SomeOtherProduct.SomeModule import SomeClass
def speak(self):
   return "ook ook eee eee eee!"
SomeClass.speak = speak

This code will overwrite/create a method called speak in the class. In Jeff Atwood's recent post on monkey patching, he showed an example in C# 3.0 which is the current language I use for work.

陈年往事 2025-01-19 08:43:21

您可以使用 lambda 将方法绑定到实例:

def run(self):
    print self._instanceString

class A(object):
    def __init__(self):
        self._instanceString = "This is instance string"

a = A()
a.run = lambda: run(a)
a.run()

输出:

This is instance string

You can use lambda to bind a method to an instance:

def run(self):
    print self._instanceString

class A(object):
    def __init__(self):
        self._instanceString = "This is instance string"

a = A()
a.run = lambda: run(a)
a.run()

Output:

This is instance string
醉酒的小男人 2025-01-19 08:43:21

我相信您正在寻找的是 setattr
使用它来设置对象的属性。

>>> def printme(s): print repr(s)
>>> class A: pass
>>> setattr(A,'printme',printme)
>>> a = A()
>>> a.printme() # s becomes the implicit 'self' variable
< __ main __ . A instance at 0xABCDEFG>

What you're looking for is setattr I believe.
Use this to set an attribute on an object.

>>> def printme(s): print repr(s)
>>> class A: pass
>>> setattr(A,'printme',printme)
>>> a = A()
>>> a.printme() # s becomes the implicit 'self' variable
< __ main __ . A instance at 0xABCDEFG>
绝不放开 2025-01-19 08:43:21

至少有两种方法可以将方法附加到没有 types.MethodType 的实例:

>>> class A:
...  def m(self):
...   print 'im m, invoked with: ', self

>>> a = A()
>>> a.m()
im m, invoked with:  <__main__.A instance at 0x973ec6c>
>>> a.m
<bound method A.m of <__main__.A instance at 0x973ec6c>>
>>> 
>>> def foo(firstargument):
...  print 'im foo, invoked with: ', firstargument

>>> foo
<function foo at 0x978548c>

1:

>>> a.foo = foo.__get__(a, A) # or foo.__get__(a, type(a))
>>> a.foo()
im foo, invoked with:  <__main__.A instance at 0x973ec6c>
>>> a.foo
<bound method A.foo of <__main__.A instance at 0x973ec6c>>

2:

>>> instancemethod = type(A.m)
>>> instancemethod
<type 'instancemethod'>
>>> a.foo2 = instancemethod(foo, a, type(a))
>>> a.foo2()
im foo, invoked with:  <__main__.A instance at 0x973ec6c>
>>> a.foo2
<bound method instance.foo of <__main__.A instance at 0x973ec6c>>

有用的链接:
数据模型 - 调用描述符
描述符操作指南 - 调用描述符

There are at least two ways for attach a method to an instance without types.MethodType:

>>> class A:
...  def m(self):
...   print 'im m, invoked with: ', self

>>> a = A()
>>> a.m()
im m, invoked with:  <__main__.A instance at 0x973ec6c>
>>> a.m
<bound method A.m of <__main__.A instance at 0x973ec6c>>
>>> 
>>> def foo(firstargument):
...  print 'im foo, invoked with: ', firstargument

>>> foo
<function foo at 0x978548c>

1:

>>> a.foo = foo.__get__(a, A) # or foo.__get__(a, type(a))
>>> a.foo()
im foo, invoked with:  <__main__.A instance at 0x973ec6c>
>>> a.foo
<bound method A.foo of <__main__.A instance at 0x973ec6c>>

2:

>>> instancemethod = type(A.m)
>>> instancemethod
<type 'instancemethod'>
>>> a.foo2 = instancemethod(foo, a, type(a))
>>> a.foo2()
im foo, invoked with:  <__main__.A instance at 0x973ec6c>
>>> a.foo2
<bound method instance.foo of <__main__.A instance at 0x973ec6c>>

Useful links:
Data model - invoking descriptors
Descriptor HowTo Guide - invoking descriptors

夜访吸血鬼 2025-01-19 08:43:21

合并 Jason Pratt 和社区 wiki 的答案,看看不同绑定方法的结果:

特别注意如何将绑定函数添加为类方法有效,但引用范围不正确。

#!/usr/bin/python -u
import types
import inspect

## dynamically adding methods to a unique instance of a class


# get a list of a class's method type attributes
def listattr(c):
    for m in [(n, v) for n, v in inspect.getmembers(c, inspect.ismethod) if isinstance(v,types.MethodType)]:
        print m[0], m[1]

# externally bind a function as a method of an instance of a class
def ADDMETHOD(c, method, name):
    c.__dict__[name] = types.MethodType(method, c)

class C():
    r = 10 # class attribute variable to test bound scope

    def __init__(self):
        pass

    #internally bind a function as a method of self's class -- note that this one has issues!
    def addmethod(self, method, name):
        self.__dict__[name] = types.MethodType( method, self.__class__ )

    # predfined function to compare with
    def f0(self, x):
        print 'f0\tx = %d\tr = %d' % ( x, self.r)

a = C() # created before modified instnace
b = C() # modified instnace


def f1(self, x): # bind internally
    print 'f1\tx = %d\tr = %d' % ( x, self.r )
def f2( self, x): # add to class instance's .__dict__ as method type
    print 'f2\tx = %d\tr = %d' % ( x, self.r )
def f3( self, x): # assign to class as method type
    print 'f3\tx = %d\tr = %d' % ( x, self.r )
def f4( self, x): # add to class instance's .__dict__ using a general function
    print 'f4\tx = %d\tr = %d' % ( x, self.r )


b.addmethod(f1, 'f1')
b.__dict__['f2'] = types.MethodType( f2, b)
b.f3 = types.MethodType( f3, b)
ADDMETHOD(b, f4, 'f4')


b.f0(0) # OUT: f0   x = 0   r = 10
b.f1(1) # OUT: f1   x = 1   r = 10
b.f2(2) # OUT: f2   x = 2   r = 10
b.f3(3) # OUT: f3   x = 3   r = 10
b.f4(4) # OUT: f4   x = 4   r = 10


k = 2
print 'changing b.r from {0} to {1}'.format(b.r, k)
b.r = k
print 'new b.r = {0}'.format(b.r)

b.f0(0) # OUT: f0   x = 0   r = 2
b.f1(1) # OUT: f1   x = 1   r = 10  !!!!!!!!!
b.f2(2) # OUT: f2   x = 2   r = 2
b.f3(3) # OUT: f3   x = 3   r = 2
b.f4(4) # OUT: f4   x = 4   r = 2

c = C() # created after modifying instance

# let's have a look at each instance's method type attributes
print '\nattributes of a:'
listattr(a)
# OUT:
# attributes of a:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x000000000230FD88>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x000000000230FD88>>
# f0 <bound method C.f0 of <__main__.C instance at 0x000000000230FD88>>

print '\nattributes of b:'
listattr(b)
# OUT:
# attributes of b:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x000000000230FE08>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x000000000230FE08>>
# f0 <bound method C.f0 of <__main__.C instance at 0x000000000230FE08>>
# f1 <bound method ?.f1 of <class __main__.C at 0x000000000237AB28>>
# f2 <bound method ?.f2 of <__main__.C instance at 0x000000000230FE08>>
# f3 <bound method ?.f3 of <__main__.C instance at 0x000000000230FE08>>
# f4 <bound method ?.f4 of <__main__.C instance at 0x000000000230FE08>>

print '\nattributes of c:'
listattr(c)
# OUT:
# attributes of c:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x0000000002313108>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x0000000002313108>>
# f0 <bound method C.f0 of <__main__.C instance at 0x0000000002313108>>

就我个人而言,我更喜欢外部 ADDMETHOD 函数路线,因为它也允许我在迭代器中动态分配新方法名称。

def y(self, x):
    pass
d = C()
for i in range(1,5):
    ADDMETHOD(d, y, 'f%d' % i)
print '\nattributes of d:'
listattr(d)
# OUT:
# attributes of d:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x0000000002303508>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x0000000002303508>>
# f0 <bound method C.f0 of <__main__.C instance at 0x0000000002303508>>
# f1 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
# f2 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
# f3 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
# f4 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>

Consolidating Jason Pratt's and the community wiki answers, with a look at the results of different methods of binding:

Especially note how adding the binding function as a class method works, but the referencing scope is incorrect.

#!/usr/bin/python -u
import types
import inspect

## dynamically adding methods to a unique instance of a class


# get a list of a class's method type attributes
def listattr(c):
    for m in [(n, v) for n, v in inspect.getmembers(c, inspect.ismethod) if isinstance(v,types.MethodType)]:
        print m[0], m[1]

# externally bind a function as a method of an instance of a class
def ADDMETHOD(c, method, name):
    c.__dict__[name] = types.MethodType(method, c)

class C():
    r = 10 # class attribute variable to test bound scope

    def __init__(self):
        pass

    #internally bind a function as a method of self's class -- note that this one has issues!
    def addmethod(self, method, name):
        self.__dict__[name] = types.MethodType( method, self.__class__ )

    # predfined function to compare with
    def f0(self, x):
        print 'f0\tx = %d\tr = %d' % ( x, self.r)

a = C() # created before modified instnace
b = C() # modified instnace


def f1(self, x): # bind internally
    print 'f1\tx = %d\tr = %d' % ( x, self.r )
def f2( self, x): # add to class instance's .__dict__ as method type
    print 'f2\tx = %d\tr = %d' % ( x, self.r )
def f3( self, x): # assign to class as method type
    print 'f3\tx = %d\tr = %d' % ( x, self.r )
def f4( self, x): # add to class instance's .__dict__ using a general function
    print 'f4\tx = %d\tr = %d' % ( x, self.r )


b.addmethod(f1, 'f1')
b.__dict__['f2'] = types.MethodType( f2, b)
b.f3 = types.MethodType( f3, b)
ADDMETHOD(b, f4, 'f4')


b.f0(0) # OUT: f0   x = 0   r = 10
b.f1(1) # OUT: f1   x = 1   r = 10
b.f2(2) # OUT: f2   x = 2   r = 10
b.f3(3) # OUT: f3   x = 3   r = 10
b.f4(4) # OUT: f4   x = 4   r = 10


k = 2
print 'changing b.r from {0} to {1}'.format(b.r, k)
b.r = k
print 'new b.r = {0}'.format(b.r)

b.f0(0) # OUT: f0   x = 0   r = 2
b.f1(1) # OUT: f1   x = 1   r = 10  !!!!!!!!!
b.f2(2) # OUT: f2   x = 2   r = 2
b.f3(3) # OUT: f3   x = 3   r = 2
b.f4(4) # OUT: f4   x = 4   r = 2

c = C() # created after modifying instance

# let's have a look at each instance's method type attributes
print '\nattributes of a:'
listattr(a)
# OUT:
# attributes of a:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x000000000230FD88>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x000000000230FD88>>
# f0 <bound method C.f0 of <__main__.C instance at 0x000000000230FD88>>

print '\nattributes of b:'
listattr(b)
# OUT:
# attributes of b:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x000000000230FE08>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x000000000230FE08>>
# f0 <bound method C.f0 of <__main__.C instance at 0x000000000230FE08>>
# f1 <bound method ?.f1 of <class __main__.C at 0x000000000237AB28>>
# f2 <bound method ?.f2 of <__main__.C instance at 0x000000000230FE08>>
# f3 <bound method ?.f3 of <__main__.C instance at 0x000000000230FE08>>
# f4 <bound method ?.f4 of <__main__.C instance at 0x000000000230FE08>>

print '\nattributes of c:'
listattr(c)
# OUT:
# attributes of c:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x0000000002313108>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x0000000002313108>>
# f0 <bound method C.f0 of <__main__.C instance at 0x0000000002313108>>

Personally, I prefer the external ADDMETHOD function route, as it allows me to dynamically assign new method names within an iterator as well.

def y(self, x):
    pass
d = C()
for i in range(1,5):
    ADDMETHOD(d, y, 'f%d' % i)
print '\nattributes of d:'
listattr(d)
# OUT:
# attributes of d:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x0000000002303508>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x0000000002303508>>
# f0 <bound method C.f0 of <__main__.C instance at 0x0000000002303508>>
# f1 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
# f2 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
# f3 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
# f4 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
看海 2025-01-19 08:43:21

由于这个问题要求非 Python 版本,因此这里是 JavaScript:

a.methodname = function () { console.log("Yay, a new method!") }

Since this question asked for non-Python versions, here's JavaScript:

a.methodname = function () { console.log("Yay, a new method!") }
没︽人懂的悲伤 2025-01-19 08:43:21

这实际上是“Jason Pratt”答案的一个插件

,尽管 Jason 的答案有效,但只有在想向类添加函数时它才有效。
当我尝试从 .py 源代码文件重新加载已经存在的方法时,它对我不起作用。

我花了很长时间才找到解决方法,但技巧似乎很简单......
1.st从源代码文件中导入代码
2.强制重新加载
3.rd使用types.FunctionType(...)将导入并绑定的方法转换为函数
您还可以传递当前的全局变量,因为重新加载的方法将位于不同的命名空间中
4.现在您可以按照“Jason Pratt”的建议继续
使用 types.MethodType(...)

示例:

# this class resides inside ReloadCodeDemo.py
class A:
    def bar( self ):
        print "bar1"
        
    def reloadCode(self, methodName):
        ''' use this function to reload any function of class A'''
        import types
        import ReloadCodeDemo as ReloadMod # import the code as module
        reload (ReloadMod) # force a reload of the module
        myM = getattr(ReloadMod.A,methodName) #get reloaded Method
        myTempFunc = types.FunctionType(# convert the method to a simple function
                                myM.im_func.func_code, #the methods code
                                globals(), # globals to use
                                argdefs=myM.im_func.func_defaults # default values for variables if any
                                ) 
        myNewM = types.MethodType(myTempFunc,self,self.__class__) #convert the function to a method
        setattr(self,methodName,myNewM) # add the method to the function

if __name__ == '__main__':
    a = A()
    a.bar()
    # now change your code and save the file
    a.reloadCode('bar') # reloads the file
    a.bar() # now executes the reloaded code

This is actually an addon to the answer of "Jason Pratt"

Although Jasons answer works, it does only work if one wants to add a function to a class.
It did not work for me when I tried to reload an already existing method from the .py source code file.

It took me for ages to find a workaround, but the trick seems simple...
1.st import the code from the source code file
2.nd force a reload
3.rd use types.FunctionType(...) to convert the imported and bound method to a function
you can also pass on the current global variables, as the reloaded method would be in a different namespace
4.th now you can continue as suggested by "Jason Pratt"
using the types.MethodType(...)

Example:

# this class resides inside ReloadCodeDemo.py
class A:
    def bar( self ):
        print "bar1"
        
    def reloadCode(self, methodName):
        ''' use this function to reload any function of class A'''
        import types
        import ReloadCodeDemo as ReloadMod # import the code as module
        reload (ReloadMod) # force a reload of the module
        myM = getattr(ReloadMod.A,methodName) #get reloaded Method
        myTempFunc = types.FunctionType(# convert the method to a simple function
                                myM.im_func.func_code, #the methods code
                                globals(), # globals to use
                                argdefs=myM.im_func.func_defaults # default values for variables if any
                                ) 
        myNewM = types.MethodType(myTempFunc,self,self.__class__) #convert the function to a method
        setattr(self,methodName,myNewM) # add the method to the function

if __name__ == '__main__':
    a = A()
    a.bar()
    # now change your code and save the file
    a.reloadCode('bar') # reloads the file
    a.bar() # now executes the reloaded code
影子是时光的心 2025-01-19 08:43:21

这个问题是几年前提出的,但是嘿,有一种简单的方法可以使用装饰器模拟函数到类实例的绑定:

def binder (function, instance):
  copy_of_function = type (function) (function.func_code, {})
  copy_of_function.__bind_to__ = instance
  def bound_function (*args, **kwargs):
    return copy_of_function (copy_of_function.__bind_to__, *args, **kwargs)
  return bound_function


class SupaClass (object):
  def __init__ (self):
    self.supaAttribute = 42


def new_method (self):
  print self.supaAttribute


supaInstance = SupaClass ()
supaInstance.supMethod = binder (new_method, supaInstance)

otherInstance = SupaClass ()
otherInstance.supaAttribute = 72
otherInstance.supMethod = binder (new_method, otherInstance)

otherInstance.supMethod ()
supaInstance.supMethod ()

在那里,当您将函数和实例传递给绑定器装饰器时,它将创建一个新函数,具有与第一个相同的代码对象。然后,该类的给定实例存储在新创建的函数的属性中。装饰器返回一个(第三个)函数,自动调用复制的函数,并将实例作为第一个参数。

总之,您得到一个模拟它绑定到类实例的函数。保持原来的功能不变。

This question was opened years ago, but hey, there's an easy way to simulate the binding of a function to a class instance using decorators:

def binder (function, instance):
  copy_of_function = type (function) (function.func_code, {})
  copy_of_function.__bind_to__ = instance
  def bound_function (*args, **kwargs):
    return copy_of_function (copy_of_function.__bind_to__, *args, **kwargs)
  return bound_function


class SupaClass (object):
  def __init__ (self):
    self.supaAttribute = 42


def new_method (self):
  print self.supaAttribute


supaInstance = SupaClass ()
supaInstance.supMethod = binder (new_method, supaInstance)

otherInstance = SupaClass ()
otherInstance.supaAttribute = 72
otherInstance.supMethod = binder (new_method, otherInstance)

otherInstance.supMethod ()
supaInstance.supMethod ()

There, when you pass the function and the instance to the binder decorator, it will create a new function, with the same code object as the first one. Then, the given instance of the class is stored in an attribute of the newly created function. The decorator return a (third) function calling automatically the copied function, giving the instance as the first parameter.

In conclusion you get a function simulating it's binding to the class instance. Letting the original function unchanged.

高速公鹿 2025-01-19 08:43:21

我觉得奇怪的是,没有人提到上面列出的所有方法都会在添加的方法和实例之间创建循环引用,导致对象在垃圾回收之前一直保持不变。有一个老技巧通过扩展对象的类来添加描述符:

def addmethod(obj, name, func):
    klass = obj.__class__
    subclass = type(klass.__name__, (klass,), {})
    setattr(subclass, name, func)
    obj.__class__ = subclass

I find it strange that nobody mentioned that all of the methods listed above creates a cycle reference between the added method and the instance, causing the object to be persistent till garbage collection. There was an old trick adding a descriptor by extending the class of the object:

def addmethod(obj, name, func):
    klass = obj.__class__
    subclass = type(klass.__name__, (klass,), {})
    setattr(subclass, name, func)
    obj.__class__ = subclass
夜灵血窟げ 2025-01-19 08:43:21

如果有什么帮助的话,我最近发布了一个名为 Gorilla 的 Python 库,以使猴子修补过程更加方便。

使用函数 needle() 来修补名为 guineapig 的模块,如下所示:

import gorilla
import guineapig
@gorilla.patch(guineapig)
def needle():
    print("awesome")

但它还可以处理更有趣的用例,如 常见问题解答来自

该代码可在 GitHub 上获取。

If it can be of any help, I recently released a Python library named Gorilla to make the process of monkey patching more convenient.

Using a function needle() to patch a module named guineapig goes as follows:

import gorilla
import guineapig
@gorilla.patch(guineapig)
def needle():
    print("awesome")

But it also takes care of more interesting use cases as shown in the FAQ from the documentation.

The code is available on GitHub.

格子衫的從容 2025-01-19 08:43:21
from types import MethodType

def method(self):
   print 'hi!'


setattr( targetObj, method.__name__, MethodType(method, targetObj, type(method)) )

这样,您就可以使用 self 指针

from types import MethodType

def method(self):
   print 'hi!'


setattr( targetObj, method.__name__, MethodType(method, targetObj, type(method)) )

With this, you can use the self pointer

水溶 2025-01-19 08:43:21

杰森普拉特发布的内容是正确的。

>>> class Test(object):
...   def a(self):
...     pass
... 
>>> def b(self):
...   pass
... 
>>> Test.b = b
>>> type(b)
<type 'function'>
>>> type(Test.a)
<type 'instancemethod'>
>>> type(Test.b)
<type 'instancemethod'>

正如您所看到的,Python 认为 b() 与 a() 没有任何不同。在 Python 中,所有方法都只是恰好是函数的变量。

What Jason Pratt posted is correct.

>>> class Test(object):
...   def a(self):
...     pass
... 
>>> def b(self):
...   pass
... 
>>> Test.b = b
>>> type(b)
<type 'function'>
>>> type(Test.a)
<type 'instancemethod'>
>>> type(Test.b)
<type 'instancemethod'>

As you can see, Python doesn't consider b() any different than a(). In Python all methods are just variables that happen to be functions.

忘羡 2025-01-19 08:43:21

如何从类的实例恢复类

class UnderWater:
    def __init__(self):
        self.net = 'underwater'

marine = UnderWater() # Instantiate the class

# Recover the class from the instance and add attributes to it.
class SubMarine(marine.__class__):  
    def __init__(self):
        super().__init__()
            self.sound = 'Sonar'
    
print(SubMarine, SubMarine.__name__, SubMarine().net, SubMarine().sound)

# Output
# (__main__.SubMarine,'SubMarine', 'underwater', 'Sonar')

How to recover a class from an instance of a class

class UnderWater:
    def __init__(self):
        self.net = 'underwater'

marine = UnderWater() # Instantiate the class

# Recover the class from the instance and add attributes to it.
class SubMarine(marine.__class__):  
    def __init__(self):
        super().__init__()
            self.sound = 'Sonar'
    
print(SubMarine, SubMarine.__name__, SubMarine().net, SubMarine().sound)

# Output
# (__main__.SubMarine,'SubMarine', 'underwater', 'Sonar')
年少掌心 2025-01-19 08:43:21

除了其他人所说的之外,我发现 __repr____str__ 方法不能在对象级别上进行猴子修补,因为 repr()str() 使用类方法,而不是本地边界对象方法:

# Instance monkeypatch
[ins] In [55]: x.__str__ = show.__get__(x)                                                                 

[ins] In [56]: x                                                                                           
Out[56]: <__main__.X at 0x7fc207180c10>

[ins] In [57]: str(x)                                                                                      
Out[57]: '<__main__.X object at 0x7fc207180c10>'

[ins] In [58]: x.__str__()                                                                                 
Nice object!

# Class monkeypatch
[ins] In [62]: X.__str__ = lambda _: "From class"                                                          

[ins] In [63]: str(x)                                                                                      
Out[63]: 'From class'

Apart from what others said, I found that __repr__ and __str__ methods can't be monkeypatched on object level, because repr() and str() use class-methods, not locally-bounded object methods:

# Instance monkeypatch
[ins] In [55]: x.__str__ = show.__get__(x)                                                                 

[ins] In [56]: x                                                                                           
Out[56]: <__main__.X at 0x7fc207180c10>

[ins] In [57]: str(x)                                                                                      
Out[57]: '<__main__.X object at 0x7fc207180c10>'

[ins] In [58]: x.__str__()                                                                                 
Nice object!

# Class monkeypatch
[ins] In [62]: X.__str__ = lambda _: "From class"                                                          

[ins] In [63]: str(x)                                                                                      
Out[63]: 'From class'
娇纵 2025-01-19 08:43:21

感谢阿图罗!
你的回答让我走上了正轨!

基于Arturo的代码,我写了一个小类:

from types import MethodType
import re
from string import ascii_letters


class DynamicAttr:
    def __init__(self):
        self.dict_all_files = {}

    def _copy_files(self, *args, **kwargs):
        print(f'copy {args[0]["filename"]} {args[0]["copy_command"]}')

    def _delete_files(self, *args, **kwargs):
        print(f'delete {args[0]["filename"]} {args[0]["delete_command"]}')

    def _create_properties(self):
        for key, item in self.dict_all_files.items():
            setattr(
                self,
                key,
                self.dict_all_files[key],
            )
            setattr(
                self,
                key + "_delete",
                MethodType(
                    self._delete_files,
                    {
                        "filename": key,
                        "delete_command": f'del {item}',
                    },
                ),
            )
            setattr(
                self,
                key + "_copy",
                MethodType(
                    self._copy_files,
                    {
                        "filename": key,
                        "copy_command": f'copy {item}',
                    },
                ),
            )
    def add_files_to_class(self, filelist: list):
        for _ in filelist:
            attr_key = re.sub(rf'[^{ascii_letters}]+', '_', _).strip('_')
            self.dict_all_files[attr_key] = _
        self._create_properties()
dy = DynamicAttr()
dy.add_files_to_class([r"C:\Windows\notepad.exe", r"C:\Windows\regedit.exe"])

dy.add_files_to_class([r"C:\Windows\HelpPane.exe", r"C:\Windows\win.ini"])
#output
print(dy.C_Windows_HelpPane_exe)
dy.C_Windows_notepad_exe_delete()
dy.C_Windows_HelpPane_exe_copy()
C:\Windows\HelpPane.exe
delete C_Windows_notepad_exe del C:\Windows\notepad.exe
copy C_Windows_HelpPane_exe copy C:\Windows\HelpPane.exe

result

这个类允许您随时添加新的属性和方法。

编辑:

这是一个更通用的解决方案:

import inspect
import re
from copy import deepcopy
from string import ascii_letters


def copy_func(f):
    if callable(f):
        if inspect.ismethod(f) or inspect.isfunction(f):
            g = lambda *args, **kwargs: f(*args, **kwargs)
            t = list(filter(lambda prop: not ("__" in prop), dir(f)))
            i = 0
            while i < len(t):
                setattr(g, t[i], getattr(f, t[i]))
                i += 1
            return g
    dcoi = deepcopy([f])
    return dcoi[0]


class FlexiblePartial:
    def __init__(self, func, this_args_first, *args, **kwargs):

        try:
            self.f = copy_func(func)  # create a copy of the function
        except Exception:
            self.f = func
        self.this_args_first = this_args_first  # where should the other (optional) arguments be that are passed when the function is called
        try:
            self.modulename = args[0].__class__.__name__  # to make repr look good
        except Exception:
            self.modulename = "self"

        try:
            self.functionname = func.__name__  # to make repr look good
        except Exception:
            try:
                self.functionname = func.__qualname__  # to make repr look good
            except Exception:
                self.functionname = "func"

        self.args = args
        self.kwargs = kwargs

        self.name_to_print = self._create_name()  # to make repr look good

    def _create_name(self):
        stra = self.modulename + "." + self.functionname + "(self, "
        for _ in self.args[1:]:
            stra = stra + repr(_) + ", "
        for key, item in self.kwargs.items():
            stra = stra + str(key) + "=" + repr(item) + ", "
        stra = stra.rstrip().rstrip(",")
        stra += ")"
        if len(stra) > 100:
            stra = stra[:95] + "...)"
        return stra

    def __call__(self, *args, **kwargs):
        newdic = {}
        newdic.update(self.kwargs)
        newdic.update(kwargs)
        if self.this_args_first:
            return self.f(*self.args[1:], *args, **newdic)

        else:

            return self.f(*args, *self.args[1:], **newdic)

    def __str__(self):
        return self.name_to_print

    def __repr__(self):
        return self.__str__()


class AddMethodsAndProperties:
    def add_methods(self, dict_to_add):
        for key_, item in dict_to_add.items():
            key = re.sub(rf"[^{ascii_letters}]+", "_", str(key_)).rstrip("_")
            if isinstance(item, dict):
                if "function" in item:  # for adding methods
                    if not isinstance(
                        item["function"], str
                    ):  # for external functions that are not part of the class
                        setattr(
                            self,
                            key,
                            FlexiblePartial(
                                item["function"],
                                item["this_args_first"],
                                self,
                                *item["args"],
                                **item["kwargs"],
                            ),
                        )

                    else:
                        setattr(
                            self,
                            key,
                            FlexiblePartial(
                                getattr(
                                    self, item["function"]
                                ),  # for internal functions - part of the class
                                item["this_args_first"],
                                self,
                                *item["args"],
                                **item["kwargs"],
                            ),
                        )
            else:  # for adding props
                setattr(self, key, item)

让我们测试一下:

class NewClass(AddMethodsAndProperties): #inherit from AddMethodsAndProperties to add the method add_methods
    def __init__(self):
        self.bubu = 5

    def _delete_files(self, file): #some random methods 
        print(f"File will be deleted: {file}")

    def delete_files(self, file):
        self._delete_files(file)

    def _copy_files(self, file, dst):
        print(f"File will be copied: {file} Dest: {dst}")

    def copy_files(self, file, dst):
        self._copy_files(file, dst)

    def _create_files(self, file, folder):
        print(f"File will be created: {file} {folder}")

    def create_files(self, file, folder):
        self._create_files(file, folder)

    def method_with_more_kwargs(self, file, folder, one_more):
        print(file, folder, one_more)
        return self


nc = NewClass()
dict_all_files = {
    r"C:\Windows\notepad.exe_delete": {
        "function": "delete_files",
        "args": (),
        "kwargs": {"file": r"C:\Windows\notepad.exe"},
        "this_args_first": True,
    },
    r"C:\Windows\notepad.exe_argsfirst": {
        "function": "delete_files",
        "args": (),
        "kwargs": {"file": r"C:\Windows\notepad.exe"},
        "this_args_first": True,
    },
    r"C:\Windows\notepad.exe_copy": {
        "function": "copy_files",
        "args": (),
        "kwargs": {
            "file": r"C:\Windows\notepad.exe",
            "dst": r"C:\Windows\notepad555.exe",
        },
        "this_args_first": True,
    },
    r"C:\Windows\notepad.exe_create": {
        "function": "create_files",
        "args": (),
        "kwargs": {"file": r"C:\Windows\notepad.exe", "folder": "c:\\windows95"},
        "this_args_first": True,
    },
    r"C:\Windows\notepad.exe_upper": {
        "function": str.upper,
        "args": (r"C:\Windows\notepad.exe",),
        "kwargs": {},
        "this_args_first": True,
    },
    r"C:\Windows\notepad.exe_method_with_more_kwargs": {
        "function": "method_with_more_kwargs",
        "args": (),
        "kwargs": {"file": r"C:\Windows\notepad.exe", "folder": "c:\\windows95"},
        "this_args_first": True,
    },
    r"C:\Windows\notepad.exe_method_with_more_kwargs_as_args_first": {
        "function": "method_with_more_kwargs",
        "args": (r"C:\Windows\notepad.exe", "c:\\windows95"),
        "kwargs": {},
        "this_args_first": True,
    },
    r"C:\Windows\notepad.exe_method_with_more_kwargs_as_args_last": {
        "function": "method_with_more_kwargs",
        "args": (r"C:\Windows\notepad.exe", "c:\\windows95"),
        "kwargs": {},
        "this_args_first": False,
    },
    "this_is_a_list": [55, 3, 3, 1, 4, 43],
}

nc.add_methods(dict_all_files)


print(nc.C_Windows_notepad_exe_delete)
print(nc.C_Windows_notepad_exe_delete(), end="\n\n")
print(nc.C_Windows_notepad_exe_argsfirst)
print(nc.C_Windows_notepad_exe_argsfirst(), end="\n\n")
print(nc.C_Windows_notepad_exe_copy)
print(nc.C_Windows_notepad_exe_copy(), end="\n\n")
print(nc.C_Windows_notepad_exe_create)
print(nc.C_Windows_notepad_exe_create(), end="\n\n")
print(nc.C_Windows_notepad_exe_upper)
print(nc.C_Windows_notepad_exe_upper(), end="\n\n")
print(nc.C_Windows_notepad_exe_method_with_more_kwargs)
print(
    nc.C_Windows_notepad_exe_method_with_more_kwargs(
        one_more="f:\\blaaaaaaaaaaaaaaaaaaaaaaaa"
    )
    .C_Windows_notepad_exe_method_with_more_kwargs(
        one_more="f:\\ASJVASDFASÇDFJASÇDJFÇASWFJASÇ"
    )
    .C_Windows_notepad_exe_method_with_more_kwargs(
        one_more="f:\\XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
    ),
    end="\n\n",
)
print(nc.C_Windows_notepad_exe_method_with_more_kwargs_as_args_first)
print(
    nc.C_Windows_notepad_exe_method_with_more_kwargs_as_args_first(
        "f:\\blaaaaaaaaaaaaaaaaaaaaaaaa"
    ),
    end="\n\n",
)
print(
    nc.C_Windows_notepad_exe_method_with_more_kwargs_as_args_first(
        "f:\\blaaaaaaaaaaaaaaaaaaaaaaaa"
    )
    .C_Windows_notepad_exe_method_with_more_kwargs_as_args_first(
        "f:\\ASJVASDFASÇDFJASÇDJFÇASWFJASÇ"
    )
    .C_Windows_notepad_exe_method_with_more_kwargs_as_args_first(
        "f:\\XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
    ),
    end="\n\n",
)
print(nc.C_Windows_notepad_exe_method_with_more_kwargs_as_args_last)
print(
    nc.C_Windows_notepad_exe_method_with_more_kwargs_as_args_last(
        "f:\\blaaaaaaaaaaaaaaaaaaaaaaaa"
    )
    .C_Windows_notepad_exe_method_with_more_kwargs_as_args_last(
        "f:\\ASJVASDFASÇDFJASÇDJFÇASWFJASÇ"
    )
    .C_Windows_notepad_exe_method_with_more_kwargs_as_args_last(
        "f:\\XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
    ),
    end="\n\n",
)
print(
    nc.C_Windows_notepad_exe_method_with_more_kwargs_as_args_last(
        "f:\\blaaaaaaaaaaaaaaaaaaaaaaaa"
    )
    .C_Windows_notepad_exe_method_with_more_kwargs_as_args_last(
        "f:\\ASJVASDFASÇDFJASÇDJFÇASWFJASÇ"
    )
    .C_Windows_notepad_exe_method_with_more_kwargs_as_args_last(
        "f:\\XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
    ),
    end="\n\n",
)
print(nc.this_is_a_list)
checkit = (
    nc.C_Windows_notepad_exe_method_with_more_kwargs_as_args_last(
        "f:\\blaaaaaaaaaaaaaaaaaaaaaaaa"
    )
    .C_Windows_notepad_exe_method_with_more_kwargs_as_args_last(
        "f:\\ASJVASDFASÇDFJASÇDJFÇASWFJASÇ"
    )
    .C_Windows_notepad_exe_method_with_more_kwargs_as_args_last(
        "f:\\XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
    )
)
print(f'nc is checkit? -> {nc is checkit}')


#output:


NewClass.delete_files(self, file='C:\\Windows\\notepad.exe')
File will be deleted: C:\Windows\notepad.exe
None


NewClass.delete_files(self, file='C:\\Windows\\notepad.exe')
File will be deleted: C:\Windows\notepad.exe
None


NewClass.copy_files(self, file='C:\\Windows\\notepad.exe', dst='C:\\Windows\\notepad555.exe')
File will be copied: C:\Windows\notepad.exe Dest: C:\Windows\notepad555.exe
None


NewClass.create_files(self, file='C:\\Windows\\notepad.exe', folder='c:\\windows95')
File will be created: C:\Windows\notepad.exe c:\windows95
None


NewClass.upper(self, 'C:\\Windows\\notepad.exe')
C:\WINDOWS\NOTEPAD.EXE


NewClass.method_with_more_kwargs(self, file='C:\\Windows\\notepad.exe', folder='c:\\windows95')
C:\Windows\notepad.exe c:\windows95 f:\blaaaaaaaaaaaaaaaaaaaaaaaa
C:\Windows\notepad.exe c:\windows95 f:\ASJVASDFASÇDFJASÇDJFÇASWFJASÇ
C:\Windows\notepad.exe c:\windows95 f:\XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
<__main__.NewClass object at 0x0000000005F199A0>


NewClass.method_with_more_kwargs(self, 'C:\\Windows\\notepad.exe', 'c:\\windows95')
C:\Windows\notepad.exe c:\windows95 f:\blaaaaaaaaaaaaaaaaaaaaaaaa
<__main__.NewClass object at 0x0000000005F199A0>


C:\Windows\notepad.exe c:\windows95 f:\blaaaaaaaaaaaaaaaaaaaaaaaa
C:\Windows\notepad.exe c:\windows95 f:\ASJVASDFASÇDFJASÇDJFÇASWFJASÇ
C:\Windows\notepad.exe c:\windows95 f:\XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
<__main__.NewClass object at 0x0000000005F199A0>


NewClass.method_with_more_kwargs(self, 'C:\\Windows\\notepad.exe', 'c:\\windows95')
f:\blaaaaaaaaaaaaaaaaaaaaaaaa C:\Windows\notepad.exe c:\windows95
f:\ASJVASDFASÇDFJASÇDJFÇASWFJASÇ C:\Windows\notepad.exe c:\windows95
f:\XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX C:\Windows\notepad.exe c:\windows95
<__main__.NewClass object at 0x0000000005F199A0>


f:\blaaaaaaaaaaaaaaaaaaaaaaaa C:\Windows\notepad.exe c:\windows95
f:\ASJVASDFASÇDFJASÇDJFÇASWFJASÇ C:\Windows\notepad.exe c:\windows95
f:\XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX C:\Windows\notepad.exe c:\windows95
<__main__.NewClass object at 0x0000000005F199A0>


[55, 3, 3, 1, 4, 43]


f:\blaaaaaaaaaaaaaaaaaaaaaaaa C:\Windows\notepad.exe c:\windows95
f:\ASJVASDFASÇDFJASÇDJFÇASWFJASÇ C:\Windows\notepad.exe c:\windows95
f:\XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX C:\Windows\notepad.exe c:\windows95


nc is checkit? -> True

Thanks to Arturo!
Your answer got me on the right track!

Based on Arturo's code, I wrote a little class:

from types import MethodType
import re
from string import ascii_letters


class DynamicAttr:
    def __init__(self):
        self.dict_all_files = {}

    def _copy_files(self, *args, **kwargs):
        print(f'copy {args[0]["filename"]} {args[0]["copy_command"]}')

    def _delete_files(self, *args, **kwargs):
        print(f'delete {args[0]["filename"]} {args[0]["delete_command"]}')

    def _create_properties(self):
        for key, item in self.dict_all_files.items():
            setattr(
                self,
                key,
                self.dict_all_files[key],
            )
            setattr(
                self,
                key + "_delete",
                MethodType(
                    self._delete_files,
                    {
                        "filename": key,
                        "delete_command": f'del {item}',
                    },
                ),
            )
            setattr(
                self,
                key + "_copy",
                MethodType(
                    self._copy_files,
                    {
                        "filename": key,
                        "copy_command": f'copy {item}',
                    },
                ),
            )
    def add_files_to_class(self, filelist: list):
        for _ in filelist:
            attr_key = re.sub(rf'[^{ascii_letters}]+', '_', _).strip('_')
            self.dict_all_files[attr_key] = _
        self._create_properties()
dy = DynamicAttr()
dy.add_files_to_class([r"C:\Windows\notepad.exe", r"C:\Windows\regedit.exe"])

dy.add_files_to_class([r"C:\Windows\HelpPane.exe", r"C:\Windows\win.ini"])
#output
print(dy.C_Windows_HelpPane_exe)
dy.C_Windows_notepad_exe_delete()
dy.C_Windows_HelpPane_exe_copy()
C:\Windows\HelpPane.exe
delete C_Windows_notepad_exe del C:\Windows\notepad.exe
copy C_Windows_HelpPane_exe copy C:\Windows\HelpPane.exe

result

This class allows you to add new attributes and methods at any time.

Edit:

Here is a more generalized solution:

import inspect
import re
from copy import deepcopy
from string import ascii_letters


def copy_func(f):
    if callable(f):
        if inspect.ismethod(f) or inspect.isfunction(f):
            g = lambda *args, **kwargs: f(*args, **kwargs)
            t = list(filter(lambda prop: not ("__" in prop), dir(f)))
            i = 0
            while i < len(t):
                setattr(g, t[i], getattr(f, t[i]))
                i += 1
            return g
    dcoi = deepcopy([f])
    return dcoi[0]


class FlexiblePartial:
    def __init__(self, func, this_args_first, *args, **kwargs):

        try:
            self.f = copy_func(func)  # create a copy of the function
        except Exception:
            self.f = func
        self.this_args_first = this_args_first  # where should the other (optional) arguments be that are passed when the function is called
        try:
            self.modulename = args[0].__class__.__name__  # to make repr look good
        except Exception:
            self.modulename = "self"

        try:
            self.functionname = func.__name__  # to make repr look good
        except Exception:
            try:
                self.functionname = func.__qualname__  # to make repr look good
            except Exception:
                self.functionname = "func"

        self.args = args
        self.kwargs = kwargs

        self.name_to_print = self._create_name()  # to make repr look good

    def _create_name(self):
        stra = self.modulename + "." + self.functionname + "(self, "
        for _ in self.args[1:]:
            stra = stra + repr(_) + ", "
        for key, item in self.kwargs.items():
            stra = stra + str(key) + "=" + repr(item) + ", "
        stra = stra.rstrip().rstrip(",")
        stra += ")"
        if len(stra) > 100:
            stra = stra[:95] + "...)"
        return stra

    def __call__(self, *args, **kwargs):
        newdic = {}
        newdic.update(self.kwargs)
        newdic.update(kwargs)
        if self.this_args_first:
            return self.f(*self.args[1:], *args, **newdic)

        else:

            return self.f(*args, *self.args[1:], **newdic)

    def __str__(self):
        return self.name_to_print

    def __repr__(self):
        return self.__str__()


class AddMethodsAndProperties:
    def add_methods(self, dict_to_add):
        for key_, item in dict_to_add.items():
            key = re.sub(rf"[^{ascii_letters}]+", "_", str(key_)).rstrip("_")
            if isinstance(item, dict):
                if "function" in item:  # for adding methods
                    if not isinstance(
                        item["function"], str
                    ):  # for external functions that are not part of the class
                        setattr(
                            self,
                            key,
                            FlexiblePartial(
                                item["function"],
                                item["this_args_first"],
                                self,
                                *item["args"],
                                **item["kwargs"],
                            ),
                        )

                    else:
                        setattr(
                            self,
                            key,
                            FlexiblePartial(
                                getattr(
                                    self, item["function"]
                                ),  # for internal functions - part of the class
                                item["this_args_first"],
                                self,
                                *item["args"],
                                **item["kwargs"],
                            ),
                        )
            else:  # for adding props
                setattr(self, key, item)

Let's test it:

class NewClass(AddMethodsAndProperties): #inherit from AddMethodsAndProperties to add the method add_methods
    def __init__(self):
        self.bubu = 5

    def _delete_files(self, file): #some random methods 
        print(f"File will be deleted: {file}")

    def delete_files(self, file):
        self._delete_files(file)

    def _copy_files(self, file, dst):
        print(f"File will be copied: {file} Dest: {dst}")

    def copy_files(self, file, dst):
        self._copy_files(file, dst)

    def _create_files(self, file, folder):
        print(f"File will be created: {file} {folder}")

    def create_files(self, file, folder):
        self._create_files(file, folder)

    def method_with_more_kwargs(self, file, folder, one_more):
        print(file, folder, one_more)
        return self


nc = NewClass()
dict_all_files = {
    r"C:\Windows\notepad.exe_delete": {
        "function": "delete_files",
        "args": (),
        "kwargs": {"file": r"C:\Windows\notepad.exe"},
        "this_args_first": True,
    },
    r"C:\Windows\notepad.exe_argsfirst": {
        "function": "delete_files",
        "args": (),
        "kwargs": {"file": r"C:\Windows\notepad.exe"},
        "this_args_first": True,
    },
    r"C:\Windows\notepad.exe_copy": {
        "function": "copy_files",
        "args": (),
        "kwargs": {
            "file": r"C:\Windows\notepad.exe",
            "dst": r"C:\Windows\notepad555.exe",
        },
        "this_args_first": True,
    },
    r"C:\Windows\notepad.exe_create": {
        "function": "create_files",
        "args": (),
        "kwargs": {"file": r"C:\Windows\notepad.exe", "folder": "c:\\windows95"},
        "this_args_first": True,
    },
    r"C:\Windows\notepad.exe_upper": {
        "function": str.upper,
        "args": (r"C:\Windows\notepad.exe",),
        "kwargs": {},
        "this_args_first": True,
    },
    r"C:\Windows\notepad.exe_method_with_more_kwargs": {
        "function": "method_with_more_kwargs",
        "args": (),
        "kwargs": {"file": r"C:\Windows\notepad.exe", "folder": "c:\\windows95"},
        "this_args_first": True,
    },
    r"C:\Windows\notepad.exe_method_with_more_kwargs_as_args_first": {
        "function": "method_with_more_kwargs",
        "args": (r"C:\Windows\notepad.exe", "c:\\windows95"),
        "kwargs": {},
        "this_args_first": True,
    },
    r"C:\Windows\notepad.exe_method_with_more_kwargs_as_args_last": {
        "function": "method_with_more_kwargs",
        "args": (r"C:\Windows\notepad.exe", "c:\\windows95"),
        "kwargs": {},
        "this_args_first": False,
    },
    "this_is_a_list": [55, 3, 3, 1, 4, 43],
}

nc.add_methods(dict_all_files)


print(nc.C_Windows_notepad_exe_delete)
print(nc.C_Windows_notepad_exe_delete(), end="\n\n")
print(nc.C_Windows_notepad_exe_argsfirst)
print(nc.C_Windows_notepad_exe_argsfirst(), end="\n\n")
print(nc.C_Windows_notepad_exe_copy)
print(nc.C_Windows_notepad_exe_copy(), end="\n\n")
print(nc.C_Windows_notepad_exe_create)
print(nc.C_Windows_notepad_exe_create(), end="\n\n")
print(nc.C_Windows_notepad_exe_upper)
print(nc.C_Windows_notepad_exe_upper(), end="\n\n")
print(nc.C_Windows_notepad_exe_method_with_more_kwargs)
print(
    nc.C_Windows_notepad_exe_method_with_more_kwargs(
        one_more="f:\\blaaaaaaaaaaaaaaaaaaaaaaaa"
    )
    .C_Windows_notepad_exe_method_with_more_kwargs(
        one_more="f:\\ASJVASDFASÇDFJASÇDJFÇASWFJASÇ"
    )
    .C_Windows_notepad_exe_method_with_more_kwargs(
        one_more="f:\\XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
    ),
    end="\n\n",
)
print(nc.C_Windows_notepad_exe_method_with_more_kwargs_as_args_first)
print(
    nc.C_Windows_notepad_exe_method_with_more_kwargs_as_args_first(
        "f:\\blaaaaaaaaaaaaaaaaaaaaaaaa"
    ),
    end="\n\n",
)
print(
    nc.C_Windows_notepad_exe_method_with_more_kwargs_as_args_first(
        "f:\\blaaaaaaaaaaaaaaaaaaaaaaaa"
    )
    .C_Windows_notepad_exe_method_with_more_kwargs_as_args_first(
        "f:\\ASJVASDFASÇDFJASÇDJFÇASWFJASÇ"
    )
    .C_Windows_notepad_exe_method_with_more_kwargs_as_args_first(
        "f:\\XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
    ),
    end="\n\n",
)
print(nc.C_Windows_notepad_exe_method_with_more_kwargs_as_args_last)
print(
    nc.C_Windows_notepad_exe_method_with_more_kwargs_as_args_last(
        "f:\\blaaaaaaaaaaaaaaaaaaaaaaaa"
    )
    .C_Windows_notepad_exe_method_with_more_kwargs_as_args_last(
        "f:\\ASJVASDFASÇDFJASÇDJFÇASWFJASÇ"
    )
    .C_Windows_notepad_exe_method_with_more_kwargs_as_args_last(
        "f:\\XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
    ),
    end="\n\n",
)
print(
    nc.C_Windows_notepad_exe_method_with_more_kwargs_as_args_last(
        "f:\\blaaaaaaaaaaaaaaaaaaaaaaaa"
    )
    .C_Windows_notepad_exe_method_with_more_kwargs_as_args_last(
        "f:\\ASJVASDFASÇDFJASÇDJFÇASWFJASÇ"
    )
    .C_Windows_notepad_exe_method_with_more_kwargs_as_args_last(
        "f:\\XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
    ),
    end="\n\n",
)
print(nc.this_is_a_list)
checkit = (
    nc.C_Windows_notepad_exe_method_with_more_kwargs_as_args_last(
        "f:\\blaaaaaaaaaaaaaaaaaaaaaaaa"
    )
    .C_Windows_notepad_exe_method_with_more_kwargs_as_args_last(
        "f:\\ASJVASDFASÇDFJASÇDJFÇASWFJASÇ"
    )
    .C_Windows_notepad_exe_method_with_more_kwargs_as_args_last(
        "f:\\XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
    )
)
print(f'nc is checkit? -> {nc is checkit}')


#output:


NewClass.delete_files(self, file='C:\\Windows\\notepad.exe')
File will be deleted: C:\Windows\notepad.exe
None


NewClass.delete_files(self, file='C:\\Windows\\notepad.exe')
File will be deleted: C:\Windows\notepad.exe
None


NewClass.copy_files(self, file='C:\\Windows\\notepad.exe', dst='C:\\Windows\\notepad555.exe')
File will be copied: C:\Windows\notepad.exe Dest: C:\Windows\notepad555.exe
None


NewClass.create_files(self, file='C:\\Windows\\notepad.exe', folder='c:\\windows95')
File will be created: C:\Windows\notepad.exe c:\windows95
None


NewClass.upper(self, 'C:\\Windows\\notepad.exe')
C:\WINDOWS\NOTEPAD.EXE


NewClass.method_with_more_kwargs(self, file='C:\\Windows\\notepad.exe', folder='c:\\windows95')
C:\Windows\notepad.exe c:\windows95 f:\blaaaaaaaaaaaaaaaaaaaaaaaa
C:\Windows\notepad.exe c:\windows95 f:\ASJVASDFASÇDFJASÇDJFÇASWFJASÇ
C:\Windows\notepad.exe c:\windows95 f:\XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
<__main__.NewClass object at 0x0000000005F199A0>


NewClass.method_with_more_kwargs(self, 'C:\\Windows\\notepad.exe', 'c:\\windows95')
C:\Windows\notepad.exe c:\windows95 f:\blaaaaaaaaaaaaaaaaaaaaaaaa
<__main__.NewClass object at 0x0000000005F199A0>


C:\Windows\notepad.exe c:\windows95 f:\blaaaaaaaaaaaaaaaaaaaaaaaa
C:\Windows\notepad.exe c:\windows95 f:\ASJVASDFASÇDFJASÇDJFÇASWFJASÇ
C:\Windows\notepad.exe c:\windows95 f:\XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
<__main__.NewClass object at 0x0000000005F199A0>


NewClass.method_with_more_kwargs(self, 'C:\\Windows\\notepad.exe', 'c:\\windows95')
f:\blaaaaaaaaaaaaaaaaaaaaaaaa C:\Windows\notepad.exe c:\windows95
f:\ASJVASDFASÇDFJASÇDJFÇASWFJASÇ C:\Windows\notepad.exe c:\windows95
f:\XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX C:\Windows\notepad.exe c:\windows95
<__main__.NewClass object at 0x0000000005F199A0>


f:\blaaaaaaaaaaaaaaaaaaaaaaaa C:\Windows\notepad.exe c:\windows95
f:\ASJVASDFASÇDFJASÇDJFÇASWFJASÇ C:\Windows\notepad.exe c:\windows95
f:\XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX C:\Windows\notepad.exe c:\windows95
<__main__.NewClass object at 0x0000000005F199A0>


[55, 3, 3, 1, 4, 43]


f:\blaaaaaaaaaaaaaaaaaaaaaaaa C:\Windows\notepad.exe c:\windows95
f:\ASJVASDFASÇDFJASÇDJFÇASWFJASÇ C:\Windows\notepad.exe c:\windows95
f:\XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX C:\Windows\notepad.exe c:\windows95


nc is checkit? -> True
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文