Python: changing methods and attributes at runtime - python

I wish to create a class in Python that I can add and remove attributes and methods. How can I acomplish that?
Oh, and please don't ask why.

This example shows the differences between adding a method to a class and to an instance.
>>> class Dog():
... def __init__(self, name):
... self.name = name
...
>>> skip = Dog('Skip')
>>> spot = Dog('Spot')
>>> def talk(self):
... print 'Hi, my name is ' + self.name
...
>>> Dog.talk = talk # add method to class
>>> skip.talk()
Hi, my name is Skip
>>> spot.talk()
Hi, my name is Spot
>>> del Dog.talk # remove method from class
>>> skip.talk() # won't work anymore
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: Dog instance has no attribute 'talk'
>>> import types
>>> f = types.MethodType(talk, skip, Dog)
>>> skip.talk = f # add method to specific instance
>>> skip.talk()
Hi, my name is Skip
>>> spot.talk() # won't work, since we only modified skip
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: Dog instance has no attribute 'talk'

I wish to create a class in Python that I can add and remove attributes and methods.
import types
class SpecialClass(object):
#classmethod
def removeVariable(cls, name):
return delattr(cls, name)
#classmethod
def addMethod(cls, func):
return setattr(cls, func.__name__, types.MethodType(func, cls))
def hello(self, n):
print n
instance = SpecialClass()
SpecialClass.addMethod(hello)
>>> SpecialClass.hello(5)
5
>>> instance.hello(6)
6
>>> SpecialClass.removeVariable("hello")
>>> instance.hello(7)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'SpecialClass' object has no attribute 'hello'
>>> SpecialClass.hello(8)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'SpecialClass' has no attribute 'hello'

A possibly interesting alternative to using types.MethodType in:
>>> f = types.MethodType(talk, puppy, Dog)
>>> puppy.talk = f # add method to specific instance
would be to exploit the fact that functions are descriptors:
>>> puppy.talk = talk.__get__(puppy, Dog)

I wish to create a class in Python that I can add and remove attributes and methods. How can I acomplish that?
You can add and remove attributes and methods to any class, and they'll be available to all instances of the class:
>>> def method1(self):
pass
>>> def method1(self):
print "method1"
>>> def method2(self):
print "method2"
>>> class C():
pass
>>> c = C()
>>> c.method()
Traceback (most recent call last):
File "<pyshell#62>", line 1, in <module>
c.method()
AttributeError: C instance has no attribute 'method'
>>> C.method = method1
>>> c.method()
method1
>>> C.method = method2
>>> c.method()
method2
>>> del C.method
>>> c.method()
Traceback (most recent call last):
File "<pyshell#68>", line 1, in <module>
c.method()
AttributeError: C instance has no attribute 'method'
>>> C.attribute = "foo"
>>> c.attribute
'foo'
>>> c.attribute = "bar"
>>> c.attribute
'bar'

you can just assign directly to the class (either by accessing the original class name or via __class__ ):
class a : pass
ob=a()
ob.__class__.blah=lambda self,k: (3, self,k)
ob.blah(5)
ob2=a()
ob2.blah(7)
will print
(3, <__main__.a instance at 0x7f18e3c345f0>, 5)
(3, <__main__.a instance at 0x7f18e3c344d0>, 7)

Simply:
f1 = lambda:0 #method for instances
f2 = lambda _:0 #method for class
class C: pass #class
c1,c2 = C(),C() #instances
print dir(c1),dir(c2)
#add to the Instances
c1.func = f1
c1.any = 1.23
print dir(c1),dir(c2)
print c1.func(),c1.any
del c1.func,c1.any
#add to the Class
C.func = f2
C.any = 1.23
print dir(c1),dir(c2)
print c1.func(),c1.any
print c2.func(),c2.any
which results in:
['__doc__', '__module__'] ['__doc__', '__module__']
['__doc__', '__module__', 'any', 'func'] ['__doc__', '__module__']
0 1.23
['__doc__', '__module__', 'any', 'func'] ['__doc__', '__module__', 'any', 'func']
0 1.23
0 1.23

another alternative, if you need to replace the class wholesale is to modify the class attribute:
>>> class A(object):
... def foo(self):
... print 'A'
...
>>> class B(object):
... def foo(self):
... print 'Bar'
...
>>> a = A()
>>> a.foo()
A
>>> a.__class__ = B
>>> a.foo()
Bar

Does the class itself necessarily need to be modified? Or is the goal simply to replace what object.method() does at a particular point during runtime?
I ask because I sidestep the problem of actually modifying the class to monkey patch specific method calls in my framework with getattribute and a Runtime Decorator on my Base inheritance object.
Methods retrieved by a Base object in getattribute are wrapped in a Runtime_Decorator that parses the method calls keyword arguments for decorators/monkey patches to apply.
This enables you to utilize the syntax object.method(monkey_patch="mypatch"), object.method(decorator="mydecorator"), and even object.method(decorators=my_decorator_list).
This works for any individual method call (I leave out magic methods), does so without actually modifying any class/instance attributes, can utilize arbitrary, even foreign methods to patch, and will work transparently on sublcasses that inherit from Base (provided they don't override getattribute of course).
import trace
def monkey_patched(self, *args, **kwargs):
print self, "Tried to call a method, but it was monkey patched instead"
return "and now for something completely different"
class Base(object):
def __init__(self):
super(Base, self).__init__()
def testmethod(self):
print "%s test method" % self
def __getattribute__(self, attribute):
value = super(Base, self).__getattribute__(attribute)
if "__" not in attribute and callable(value):
value = Runtime_Decorator(value)
return value
class Runtime_Decorator(object):
def __init__(self, function):
self.function = function
def __call__(self, *args, **kwargs):
if kwargs.has_key("monkey_patch"):
module_name, patch_name = self._resolve_string(kwargs.pop("monkey_patch"))
module = self._get_module(module_name)
monkey_patch = getattr(module, patch_name)
return monkey_patch(self.function.im_self, *args, **kwargs)
if kwargs.has_key('decorator'):
decorator_type = str(kwargs['decorator'])
module_name, decorator_name = self._resolve_string(decorator_type)
decorator = self._get_decorator(decorator_name, module_name)
wrapped_function = decorator(self.function)
del kwargs['decorator']
return wrapped_function(*args, **kwargs)
elif kwargs.has_key('decorators'):
decorators = []
for item in kwargs['decorators']:
module_name, decorator_name = self._resolve_string(item)
decorator = self._get_decorator(decorator_name, module_name)
decorators.append(decorator)
wrapped_function = self.function
for item in reversed(decorators):
wrapped_function = item(wrapped_function)
del kwargs['decorators']
return wrapped_function(*args, **kwargs)
else:
return self.function(*args, **kwargs)
def _resolve_string(self, string):
try: # attempt to split the string into a module and attribute
module_name, decorator_name = string.split(".")
except ValueError: # there was no ".", it's just a single attribute
module_name = "__main__"
decorator_name = string
finally:
return module_name, decorator_name
def _get_module(self, module_name):
try: # attempt to load the module if it exists already
module = modules[module_name]
except KeyError: # import it if it doesn't
module = __import__(module_name)
finally:
return module
def _get_decorator(self, decorator_name, module_name):
module = self._get_module(module_name)
try: # attempt to procure the decorator class
decorator_wrap = getattr(module, decorator_name)
except AttributeError: # decorator not found in module
print("failed to locate decorators %s for function %s." %\
(kwargs["decorator"], self.function))
else:
return decorator_wrap # instantiate the class with self.function
class Tracer(object):
def __init__(self, function):
self.function = function
def __call__(self, *args, **kwargs):
tracer = trace.Trace(trace=1)
tracer.runfunc(self.function, *args, **kwargs)
b = Base()
b.testmethod(monkey_patch="monkey_patched")
b.testmethod(decorator="Tracer")
#b.testmethod(monkey_patch="external_module.my_patch")
The downside to this approach is getattribute hooks all access to attributes, so the checking of and potential wrapping of methods occurs even for attributes that are not methods + won't be utilizing the feature for the particular call in question. And using getattribute at all is inherently somewhat complicated.
The actual impact of this overhead in my experience/for my purposes has been negligible, and my machine runs a dual core Celeron. The previous implementation I used introspected methods upon object init and bound the Runtime_Decorator to methods then. Doing things that way eliminated the need to utilize getattribute and reduced the overhead mentioned previously... however, it also breaks pickle (maybe not dill) and is less dynamic then this approach.
The only use cases I have actually come across "in the wild" with this technique were with timing and tracing decorators. However, the possibilities it opens up are extremely wide ranging.
If you have a preexisting class that cannot be made to inherit from a different base (or utilize the technique it's own class definition or in it's base class'), then the whole thing simply does not apply to your issue at all unfortunately.
I don't think setting/removing non-callable attributes on a class at runtime is necessarily so challenging? unless you want classes that inherit from the modified class to automatically reflect the changes in themselves as well... That'd be a whole 'nother can o' worms by the sound of it though.

Related

Make function in class only accessible without calling the class

Let's say I have this class:
class A:
def __init__(self, a):
self.a = a
#classmethod
def foo(self):
return 'hello world!'
I use #classmethod, so that I can directly call the function without calling the class:
>>> A.foo()
'hello world!'
>>>
But now I am wondering, since I still can access it with calling the class:
>>> A(1).foo()
'hello world!'
>>>
Would I be able to make it that it would raise an error if the function foo is called from a called class. And only let it to be called without calling the class, like A.foo().
So if I do:
A(1).foo()
It should give an error.
The functionality of how classmethod, staticmethod and in fact normal methods are lookedup / bound is implemented via descriptors. Similarly, one can define a descriptor that forbids lookup/binding on an instance.
A naive implementation of such a descriptor checks whether it is looked up via an instance and raises an error in this case:
class NoInstanceMethod:
"""Descriptor to forbid that other descriptors can be looked up on an instance"""
def __init__(self, descr, name=None):
self.descr = descr
self.name = name
def __set_name__(self, owner, name):
self.name = name
def __get__(self, instance, owner):
# enforce the instance cannot look up the attribute at all
if instance is not None:
raise AttributeError(f"{type(instance).__name__!r} has no attribute {self.name!r}")
# invoke any descriptor we are wrapping
return self.descr.__get__(instance, owner)
This can be applied on top of other descriptors to prevent them from being looked up on an instance. Prominently, it can be combined with classmethod or staticmethod to prevent using them on an instance:
class A:
def __init__(self, a):
self.a = a
#NoInstanceMethod
#classmethod
def foo(cls):
return 'hello world!'
A.foo() # Stdout: hello world!
A(1).foo() # AttributeError: 'A' object has no attribute 'foo'
The above NoInstanceMethod is "naive" in that it does not take care of propagating descriptor calls other than __get__ to its wrapped descriptor. For example, one could propagate __set_name__ calls to allow the wrapped descriptor to know its name.
Since descriptors are free to (not) implement any of the descriptor methods, this can be supported but needs appropriate error handling. Extend the NoInstanceMethod to support whatever descriptor methods are needed in practice.
A workaround is to override its value upon initialization of a class object to make sure it wouldn't be called from self.
def raise_(exc):
raise exc
class A:
STRICTLY_CLASS_METHODS = [
"foo",
]
def __init__(self, a):
self.a = a
for method in self.STRICTLY_CLASS_METHODS:
# Option 1: Using generator.throw() to raise exception. See https://www.python.org/dev/peps/pep-0342/#new-generator-method-throw-type-value-none-traceback-none
# setattr(self, method, lambda *args, **kwargs: (_ for _ in ()).throw(AttributeError(method)))
# Option 2: Using a function to raise exception
setattr(self, method, lambda *args, **kwargs: raise_(AttributeError(method)))
#classmethod
def foo(cls):
return 'hello world!'
def bar(self):
return 'hola mundo!', self.a
Output
>>> A.foo()
'hello world!'
>>> a = A(123)
>>> a.bar()
('hola mundo!', 123)
>>> a.foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 11, in <lambda>
File "<stdin>", line 2, in raise_
AttributeError: foo
>>> a.bar()
('hola mundo!', 123)
>>> A(45).bar()
('hola mundo!', 45)
>>> A(6789).foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 11, in <lambda>
File "<stdin>", line 2, in raise_
AttributeError: foo
>>> A.foo()
'hello world!'

Python 3: remove attributes from class before instancing it

I'm working with Python3, and I have a really heavy class with many functions as attributes:
Class A (object):
def __init__(self):
...
def method1(self):
...
def method2(self):
...
...
def methodN(self):
...
I would like to create an instance of class A that only has method1, for example. How could I do this?
Using inheritance, though it might be the most technically correct way, is not an option in my case - I can't modify the codebase so much.
I thought about decorating the class and deleting its attributes before __init__ is called, but I'm not even sure where to start tackling this. Any ideas?
You can modify the __getattribute__ method of the class to disallow access to those attributes (via normal instance.attribute access)
class A (object):
def __init__(self, x):
self.x = x
def method1(self):
...
def method2(self):
...
def __getattribute__(self, name):
if object.__getattribute__(self, 'x'):
if name == 'method2':
raise AttributeError("Cannot access method2 is self.x is True")
return object.__getattribute__(self, name)
>>> a = A(False)
>>> a.method1
<bound method A.method1 of <__main__.A object at 0x000001E25992F248>>
>>> a.method2
<bound method A.method2 of <__main__.A object at 0x000001E25992F248>>
>>> b = A(True)
>>> b.method1
<bound method A.method1 of <__main__.A object at 0x000001E25992F2C8>>
>>> b.method2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 11, in __getattribute__
AttributeError: Cannot access method2 is self.x is True
Obviously, this gets pretty unwieldy and violates a lot of assumptions about what it means to be an instance of a class. I can't think of a good reason to do this in real code, as you can still access the methods through object.__getattribute__(b, 'method2')

Is there any way to create a Python class method that does NOT pollute the attribute namespace of its instances?

I want to provide a method that can be used on a Python 2.7 class object, but does not pollute the attribute namespace of its instances. Is there any way to do this?
>>> class Foo(object):
... #classmethod
... def ugh(cls):
... return 33
...
>>> Foo.ugh()
33
>>> foo = Foo()
>>> foo.ugh()
33
You could subclass the classmethod descriptor:
class classonly(classmethod):
def __get__(self, obj, type):
if obj: raise AttributeError
return super(classonly, self).__get__(obj, type)
This is how it would behave:
class C(object):
#classonly
def foo(cls):
return 42
>>> C.foo()
42
>>> c=C()
>>> c.foo()
AttributeError
This desugars to the descriptor call (rather, it is invoked by the default implementation of __getattribute__):
>>> C.__dict__['foo'].__get__(None, C)
<bound method C.foo of <class '__main__.C'>>
>>> C.__dict__['foo'].__get__(c, type(c))
AttributeError
Required reading: Data Model — Implementing Descriptors and Descriptor HowTo Guide.
ugh is not in the namespace:
>>> foo.__dict__
{}
but the rules for attribute lookup fall back to the type of the instance for missing names. You can override Foo.__getattribute__ to prevent this.
class Foo(object):
#classmethod
def ugh(cls):
return 33
def __getattribute__(self, name):
if name == 'ugh':
raise AttributeError("Access to class method 'ugh' block from instance")
return super(Foo,self).__getattribute__(name)
This produces:
>>> foo = Foo()
>>> foo.ugh()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "tmp.py", line 8, in __getattribute__
raise AttributeError("Access to class method 'ugh' block from instance")
AttributeError: Access to class method 'ugh' block from instance
>>> Foo.ugh()
33
You must use __getattribute__, which is called unconditionally on any attribute access, rather than __getattr__, which is only called after the normal lookup (which includes checking the type's namespace) fails.
Python has quasi-private variables that use name-munging to reduce accidental access. Methods and object variables of the form __name are converted to _ClassName__name. Python automatically changes the name when compiling methods on the class but doesn't change the name for subclasses.
I can use the private method in a class
>>> class A(object):
... def __private(self):
... print('boo')
... def hello(self):
... self.__private()
...
>>>
>>> A().hello()
boo
But not outside the class
>>> A().__private()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute '__private'
>>>
Or in subclasses
>>> class B(A):
... def hello2(self):
... self.__private()
...
>>>
>>> B().hello()
boo
>>> B().hello2()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in hello2
AttributeError: 'B' object has no attribute '_B__private'
Yes, you can create the method in the metaclass.
class FooMeta(type):
# No #classmethod here
def ugh(cls):
return 33
class Foo(object):
__metaclass__ = FooMeta
Foo.ugh() # returns 33
Foo().ugh() # AttributeError
Note that metaclasses are a power feature, and their use is discouraged if unnecessary. In particular, multiple inheritance requires special care if the parent classes have different metaclasses.

How to lock a method in python (prevent it to be overwritten)?

In this example, I would like to avoid the # Oops eventuality.
def foo():
return "foo"
class MyClass(object):
def __init__(self):
setattr(self, 'foo', foo)
def bar(self):
return "bar"
-
>>> x = MyClass()
>>> x.foo()
>>> x.foo = 2 # Oops
>>> x.foo()
TypeError: 'int' object is not callable
How can I prevent my methods to be overwritten by mistake?
Make x.foo a property, without specifying a setter. However it is quite tricky to do it dynamically:
def add_property(inst, name, method):
'''
Adds a property to a class instance.
Property must be added to the CLASS.
'''
cls = type(inst)
if not hasattr(cls, '__perinstance'):
cls = type(cls.__name__, (cls,), {})
cls.__perinstance = True
inst.__class__ = cls
setattr(cls, name, property(method))
And then instead of just doing setattr do it like this:
class MyClass(object):
def __init__(self):
add_property(self, 'foo', lambda _ : 2)
(for more realistic use, replace the lambda function with the
function or method returning the value for foo)
Output:
>>> o=MyClass()
>>> o.foo
2
>>> o.foo=3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
>>>
You can check if passed to setattr attribute name is already exists in class (and instance if needed) __dict__ and do not rewrite it in that case:
class MyClass(object):
def __setattr__(self, name, value):
if name not in self.__class__.__dict__ and name not in self.__dict__:
super(MyClass, self).__setattr__(name, value)
test:
>>> x = MyClass()
>>> x.foo = foo # equal to your __init__ setattr call
>>> x.foo()
'foo'
>>> x.foo = 2
>>> x.foo()
'foo'

How can I know from where the method is called?

I'm trying clean implement of Objective-C's category in Python, and found this answer to similar question of mine. I copied the code below:
categories.py
class category(object):
def __init__(self, mainModule, override = True):
self.mainModule = mainModule
self.override = override
def __call__(self, function):
if self.override or function.__name__ not in dir(self.mainModule):
setattr(self.mainModule, function.__name__, function)
But I do not want to waste namespace.
By using this `categories', there remains a variable as NoneType object like below:
>>> from categories import category
>>> class Test(object):
... pass
...
>>> #category(Test)
... def foobar(self, msg):
... print msg
...
>>> test = Test()
>>> test.foobar('hello world')
hello world
>>> type(foobar)
<type 'NoneType'>
>>>
I want it to be like below
>>> from categories import category
>>> class Test(object):
... pass
...
>>> #category(Test)
... def foobar(self, msg):
... print msg
...
>>> test = Test()
>>> test.foobar('hello world')
hello world
>>> type(foobar)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'foobar' is not defined
>>>
Is there anyway to delete it automatically like below?
def __call__(self, function):
if self.override or function.__name__ not in dir(self.mainModule):
setattr(self.mainModule, function.__name__, function)
del(somewhere.function.__name__)
I found that sys._getframe give me some useful information. But I couldn't make it by myself.
No, there's no way to automatically do that. You would have to manually delete the name afterwards. category here is a decorator, which means that
#category(Test)
def f():
...
is the same as
def f():
...
f = category(Test)(f)
Even if, from inside category, you could delete the name in the outer scope, it wouldn't be enough, because that name is rebound after the decorator executes.
The code that you linked to borders on an abuse of the decorator syntax. Decorators are meant to provide a way to modify or extend the function they decorate, but that code relies on side-effects of the decorator (namely, assigning the function as a method of a class), and then discards the function. But it can only "discard" it by returning None, so None remains bound to the function's name, as you saw.
I would recommend you follow the advice of the highest-voted answer on that question, and simply assign new methods to your classes. There is no real need for an "infrastructure" like categories in Python, because you can just directly add new methods to existing classes whenever you want.
While I completely agree with what BrenBarn said, you could split the function removal into a later step. The problem is that after the decorator executed, the variable is reassigned. So you cannot perform the removal within the decorator itself.
You could however remember the functions and remove them from the module at a later point.
class category(object):
functions = []
def __init__(self, mainModule, override = True):
self.mainModule = mainModule
self.override = override
def __call__(self, function):
if self.override or function.__name__ not in dir(self.mainModule):
setattr(self.mainModule, function.__name__, function)
self.functions.append((inspect.getmodule(function), function.__name__))
return self.dummy
#staticmethod
def dummy():
pass
#classmethod
def cleanUp(cls):
for module, name in cls.functions:
if hasattr(module, name) and getattr(module, name) == cls.dummy:
delattr(module, name)
cls.functions = []
This category type will remember the functions it decorates and stores the names and modules they belong to for a later cleanup. The decorator also returns a special dummy function so that the cleanup can ensure that the variable was not reassigned later.
>>> class Test(object): pass
>>> #category(Test)
def foobar(self, msg):
print(msg)
>>> #category(Test)
def hello_world(self):
print('Hello world')
>>> test = Test()
>>> test.foobar('xy')
xy
>>> test.hello_world()
Hello world
>>> type(foobar)
<class 'function'>
>>> type(hello_world)
<class 'function'>
>>> category.cleanUp()
>>> type(foobar)
Traceback (most recent call last):
File "<pyshell#26>", line 1, in <module>
type(foobar)
NameError: name 'foobar' is not defined
>>> type(hello_world)
Traceback (most recent call last):
File "<pyshell#27>", line 1, in <module>
type(hello_world)
NameError: name 'hello_world' is not defined

Categories

Resources