Can you modify a package's base class in python? - python

I've installed a python package (schematic), which has a number of classes extended from a base class.
class BaseType(object):
def __init__(self, required=False, default=None ...)
...
class StringType(BaseType):
...
class IntType(BaseType):
...
I would like to be able to modify the BaseType class, so it would accept additional constructor variables.
I know I could define my own classes based on these, but I was wondering if there's actually a way in Python to modify just the base class?
Thank you, Ben

Of course you can. Simply do BaseClass.__init__ = your_new_init. This does not work if the BaseClass is implemented in C however(and I believe you cannot reliably change a special method of a class implemented in C; you could do this writing in C yourself).
I believe what you want to do is a huge hack, that will only cause problems, so I strongly advise you to not replace __init__ of a base class that you didn't even write.
An example:
In [16]: class BaseClass(object):
...: def __init__(self, a, b):
...: self.a = a
...: self.b = b
...:
In [17]: class A(BaseClass): pass
In [18]: class B(BaseClass): pass
In [19]: BaseClass.old_init = BaseClass.__init__ #save old init if you plan to use it
In [21]: def new_init(self, a, b, c):
...: # calling __init__ would cause infinite recursion!
...: BaseClass.old_init(self, a, b)
...: self.c = c
In [22]: BaseClass.__init__ = new_init
In [23]: A(1, 2) # triggers the new BaseClass.__init__ method
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-23-09f95d33d46f> in <module>()
----> 1 A(1, 2)
TypeError: new_init() missing 1 required positional argument: 'c'
In [24]: A(1, 2, 3)
Out[24]: <__main__.A at 0x7fd5f29f0810>
In [25]: import numpy as np
In [26]: np.ndarray.__init__ = lambda self: 1 # doesn't work as expected
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-26-d743f6b514fa> in <module>()
----> 1 np.ndarray.__init__ = lambda self: 1
TypeError: can't set attributes of built-in/extension type 'numpy.ndarray'

You can probably edit the sourcefiles where the base class is defined, or make a copy of the package and edit the source for your specific project.
See also: How do I find the location of my Python site-packages directory?

Related

Purposely trying to break Python class inheritance, don't understand why it breaks this way

I'm exploring the limits of Python class inheritance, so I wrote a small test to see how much I can get away with - redeclaring properties and overriding functions.
class A:
val : int = 3
def foo(x: int):
print(x)
class B(A):
val : str = 'python'
def foo(x: str):
print(x)
a = A()
b = B()
a.foo(5)
b.foo('test')
print(a.val)
print(b.val)
The resulting output is surprising. I would have expected some kind of exception for redeclaring the property, but instead I get:
Traceback (most recent call last):
File "c:\Development\Playground\hello.py", line 12, in <module>
a.foo(5)
TypeError: A.foo() takes 1 positional argument but 2 were given
I don't see how it interpreting that I am providing two positional arguments for a.foo(5). Granted I am trying to break it but I still want to understand.
You need a self parameter for instance methods.
class A:
val : int = 3
def foo(self, x: int):
print(x)
class B(A):
val : str = 'python'
def foo(self, x: str):
print(x)
a = A()
b = B()
a.foo(5)
b.foo('test')
print(a.val)
print(b.val)
Output:
5
test
3
python
Now that I know you are not looking for a fix, but an idea about what the interpreter is doing I can walk you through one line of your code:
a.foo(5)
This line is just a nice way for us programmers to express the idea of calling a method (foo) on an instance (a). This is syntactic sugar and I like to think of the interpreter transforming that text to this text:
A.foo(a, 5)
and then compiling that. Now you can see, when you compare that line of code to the method that you defined: def foo(x: int): that the interpreter is going to say that the method takes one positional argument (x) but you are giving it two: (a, 5)
I think the error is produced because self was automatically passed since it's a function of a class so your functions have to take self as their first argument
class A:
val : int = 3
def foo(self, x: int):
print(x)
class B(A):
val : str = 'python'
def foo(self, x: str):
print(x)

Possible to call stateful function in a class in Python?

Is it possible to directly set a certain value and get it in a class by any mean(inheritance, metaclass, class decorator) in Python? class B must not be polluted by set('a') in A.
import sys
class A:
set('a')
get()
# -> 'a'
print(sys.modules[__name__])
# => <module '__main__'>
class B:
get()
# -> None
Assuming you know what you are doing, yes, it is possible to set values with programmatic names in a class body, and retrieve then, and have these values restricted to that class body.
All you have to do is to use thelocals() call to get the namespace dictionary, and use that dictionary to hold your values:
class A:
locals()["key"] = "a"
print(locals()["key"] )
This will print "a",
and obviously, this value won't be part of a class B namespace.
If you want just to store values without associating then
with a name, the stackfull project provide a
push and pop calls that will behave just like
you proposed in your example -
but if you try a pop() withour a previous push
in the same scope, you will get an error due to
a stack underflow:
In [4]: !pip install stackfull
Collecting stackfull
...
Successfully installed stackfull-1.0.0
In [5]: from stackfull import push, pop
In [6]: class A:
...: push("a")
...: push("b")
...: print(pop())
...: print(pop())
...:
b
a
In [7]: class B:
...: pop()
...:
...
StackUnderflowError:
(Disclaimer - I am the author of the stackfull package. I never really needed
anything beyond what is already there, so it has not been updated in a long time - and with the "walrus" operator in Python 3.8, it is no longer that useful)
Ok - so I listed this because it resembles the code in your question, but chances are you need something more mainstream, like writing your code in methods, and set instance attributes.
These two independent classes don't reference each other, so as long as you don't pass the same object to both, then neither will 'pollute' the other. To set a value and get a value on an object, you could use something like this:
class A:
def __init__(self):
self.letter = None
def set_value(self, letter):
self.letter = letter
def get_value(self):
return self.letter if self.letter else '(none set)'
class B:
def __init__(self):
self.letter = None
def set_value(self, letter):
self.letter = letter
def get_value(self):
return self.letter if self.letter else '(none set)'
>>> a = A()
>>> aa = A()
>>> b = B()
>>> a.set_value('z')
>>> print(f'values in a, aa, and b are: {a.get_value()}, {aa.get_value()}, {b.get_value()}')
values in a, aa, and b are: z, (none set), (none set)
So as you can see, setting the a object's letter doesn't set it in b (a different class), or in aa, which is a different instance of the same A class. Hope that helps!
Happy Coding!

How to call a function from another class in python

class A:
def add_f(self,a,b):
return a+b
class B:
def sum_f(self,a,b,c):
return A.add_f(a,b) + c
B1= B()
print B1.sum_f(1,2,3)
Traceback (most recent call last):
File "C:/Users/wxu/Documents/test.py", line 10, in <module>
print B1.sum_f(1,2,3)
File "C:/Users/wxu/Documents/test.py", line 7, in sum_f
return A.add_f(a,b) + c
TypeError: unbound method add_f() must be called with A instance as first argument (got int instance instead)
When I don't have the self for sum_f(self,a,b,c), it gave me this error:
Traceback (most recent call last):
File "C:/Users/wxu/Documents/test.py", line 10, in <module>
print test1.sum_f(1,2,3)
TypeError: sum_f() takes exactly 3 arguments (4 given)
Why is that?
And how to call function add_f from class A in class B? Thank you for your help.
There was a few things.
When you define class methods, they must have self as the first parameter.
The part where you had an error is where you tried to call B as a variable. B is a class, and you must call it like any other class. This also applies when you are calling A() in class B.
Revised code:
class A:
def add_f(self, a, b):
return a + b
class B:
def sum_f(self, a, b, c):
return A().add_f(a, b) + c
print B().sum_f(1, 2, 3)
Update:
Thanks for taking my advice but you're still missing something. In class B you call a method from class A, but you need parentheses for that too! In class B, call class A as such:
A().add_f(a, b)
Simple fixes, you just need to open and close the brackets on your class declarations, so class B becomes class B() (Same thing applied to class A). Also, you need to add some kind of variable such as self as the first argument of each method declared within a class. You never need to pass anything as this first argument to fill self, as python will do it for you.
As for your A.add_f issue, you just need to do as you did with B, and instantiate it an A object to something like A1, and then call the method of A1.

Assigning method to object at runtime in Python

I'm trying to do the Javascript equivalent in Python:
a.new_func = function(arg1, arg2) {
var diff = arg1 - arg2;
return diff * diff;
}
Right now, the way I'm doing this is by defining the method first, and then assigning it, but my question is whether or not Python allows a shorthand to do the assigning and the defining part in the same line. Something like this:
a.new_func = def new_func(arg1, arg2):
diff = arg1 - arg2
return diff * diff
Instead of this:
def new_func(arg1, arg2):
diff = arg1 - arg2
return diff * diff
a.new_func = new_func
I realize the difference is not major, but am still interested to know whether or not it's possible.
Python supports no such syntax.
I suppose if you wanted, you could write a decorator. It might look a bit nicer:
def method_of(instance):
def method_adder(function):
setattr(instance, function.__name__, function)
return function
return method_adder
#method_of(a)
def new_func(arg1, arg2):
stuff()
Or if you want the method to have access to self:
def method_of(instance):
def method_adder(function):
setattr(instance, function.__name__, function.__get__(instance))
return function
return method_adder
#method_of(a)
def new_func(self, arg1, arg2):
stuff()
The closest thing to what you're looking for are lambda expressions, which are not as easy to use as properly written functions:
a.new_function = lambda arg1,arg2 : (arg1-arg2)**2
However, in almost all cases, defining a function, and assigning it, the way you have done in your example is the way to go
You should notice that an instance method is not a lambda.
For instance, lets do a simple experiment in IPython
In [12]: class A:
....: def f(self):
....: return 1
....:
In [13]: A.f.__class__
Out[13]: instancemethod
In [14]: another_f = lambda self: 1
In [15]: another_f.__class__
Out[15]: function
Trying to bind an attribute to a lambda will fail miserably when calling it.
In [27]: an_instance = A()
In [28]: an_instance.g = lambda self: 2
In [29]: an_instance.g()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-29-5122c91d3e8f> in <module>()
----> 1 an_instance.g()
TypeError: <lambda>() takes exactly 1 argument (0 given)
What you should do, instead, is wrapping the lambda with types.MethodType
In [31]: an_instance.g = types.MethodType(lambda self: 2, an_instance)
In [32]: an_instance.g()
Out[32]: 2
There is some weird magic happening behind called descriptors. In my opinion, this is not a quite OO solution, but... Well, if you want to know more about it, here is a link http://www.cafepy.com/article/python_attributes_and_methods/python_attributes_and_methods.html
So I am assuming you want to know that how to define the function runtime.
May be something similar in the link
create function dynamically

Convert partial function to method in python

Consider the following (broken) code:
import functools
class Foo(object):
def __init__(self):
def f(a,self,b):
print a+b
self.g = functools.partial(f,1)
x=Foo()
x.g(2)
What I want to do is take the function f and partially apply it, resulting in a function g(self,b). I would like to use this function as a method, however this does not currently work and instead I get the error
Traceback (most recent call last):
File "test.py", line 8, in <module>
x.g(2)
TypeError: f() takes exactly 3 arguments (2 given)
Doing x.g(x,2) however works, so it seem the issue is that g is considered a "normal" function instead of a method of the class. Is there a way to get x.g to behave like a method (i.e implicitly pass the self parameter) instead of a function?
There are two issues at hand here. First, for a function to be turned into a method it must be stored on the class, not the instance. A demonstration:
class Foo(object):
def a(*args):
print 'a', args
def b(*args):
print 'b', args
Foo.b = b
x = Foo()
def c(*args):
print 'c', args
x.c = c
So a is a function defined in the class definition, b is a function assigned to the class afterwards, and c is a function assigned to the instance. Take a look at what happens when we call them:
>>> x.a('a will have "self"')
a (<__main__.Foo object at 0x100425ed0>, 'a will have "self"')
>>> x.b('as will b')
b (<__main__.Foo object at 0x100425ed0>, 'as will b')
>>> x.c('c will only recieve this string')
c ('c will only recieve this string',)
As you can see there is little difference between a function defined along with the class, and one assigned to it later. I believe there is actually no difference as long as there is no metaclass involved, but that is for another time.
The second problem comes from how a function is actually turned into a method in the first place; the function type implements the descriptor protocol. (See the docs for details.) In a nutshell, the function type has a special __get__ method which is called when you perform an attribute lookup on the class itself. Instead of you getting the function object, the __get__ method of that function object is called, and that returns a bound method object (which is what supplies the self argument).
Why is this a problem? Because the functools.partial object is not a descriptor!
>>> import functools
>>> def f(*args):
... print 'f', args
...
>>> g = functools.partial(f, 1, 2, 3)
>>> g
<functools.partial object at 0x10042f2b8>
>>> g.__get__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'functools.partial' object has no attribute '__get__'
There are a number of options you have at this point. You can explicitly supply the self argument to the partial:
import functools
class Foo(object):
def __init__(self):
def f(self, a, b):
print a + b
self.g = functools.partial(f, self, 1)
x = Foo()
x.g(2)
...or you would imbed the self and value of a in a closure:
class Foo(object):
def __init__(self):
a = 1
def f(b):
print a + b
self.g = f
x = Foo()
x.g(2)
These solutions are of course assuming that there is an as yet unspecified reason for assigning a method to the class in the constructor like this, as you can very easily just define a method directly on the class to do what you are doing here.
Edit: Here is an idea for a solution assuming the functions may be created for the class, instead of the instance:
class Foo(object):
pass
def make_binding(name):
def f(self, *args):
print 'Do %s with %s given %r.' % (name, self, args)
return f
for name in 'foo', 'bar', 'baz':
setattr(Foo, name, make_binding(name))
f = Foo()
f.foo(1, 2, 3)
f.bar('some input')
f.baz()
Gives you:
Do foo with <__main__.Foo object at 0x10053e3d0> given (1, 2, 3).
Do bar with <__main__.Foo object at 0x10053e3d0> given ('some input',).
Do baz with <__main__.Foo object at 0x10053e3d0> given ().
This will work. But I'm not sure if this is what you are looking for
class Foo(object):
def __init__(self):
def f(a,self,b):
print a+b
self.g = functools.partial(f,1, self) # <= passing `self` also.
x = Foo()
x.g(2)
this is simply a concrete example of what i believe is the most correct (and therefore pythonic :) way to solve -- as the best solution (definition on a class!) was never revealed -- #MikeBoers explanations are otherwise solid.
i've used this pattern quite a bit (recently for an proxied API), and it's survived untold production hours without the slightest irregularity.
from functools import update_wrapper
from functools import partial
from types import MethodType
class Basic(object):
def add(self, **kwds):
print sum(kwds.values())
Basic.add_to_one = MethodType(
update_wrapper(partial(Basic.add, a=1), Basic.add),
None,
Basic,
)
x = Basic()
x.add(a=1, b=9)
x.add_to_one(b=9)
...yields:
10
10
...the key take-home-point here is MethodType(func, inst, cls), which creates an unbound method from another callable (you can even use this to chain/bind instance methods to unrelated classes... when instantiated+called the original instance method will receive BOTH self objects!)
note the exclusive use of keyword arguments! while there might be a better way to handle, args are generally a PITA because the placement of self becomes less predictable. also, IME anyway, using *args, and **kwds in the bottom-most function has proven very useful later on.
functools.partialmethod() is available since python 3.4 for this purpose.
import functools
class Foo(object):
def __init__(self):
def f(a,self,b):
print a+b
self.g = functools.partialmethod(f,1)
x=Foo()
x.g(2)

Categories

Resources