This question already has answers here:
Local variables in nested functions
(4 answers)
Closed 2 years ago.
I have a Python class MyObject (a subclass of tuple) and another class for a set of these objects, MyObjectSet (a subclass of set). I’d like that, for any non-builtin method that I define for MyObject, a method of the same name be defined for MyObjectSet with value equal to the sum of the method over the contents of the MyObjectSet.
I had thought that something like the code below would work, but the result doesn’t match my intended outcome. In practice MyObject and MyObjectSet have a lot more to them and are justified.
class MyObject(tuple):
def stat_1(self):
return len(self)
def stat_2(self):
return sum(self)
class MyObjectSet(set):
pass
for stat_name in dir(MyObject):
if not stat_name.startswith("__"):
stat_func = getattr(MyObject, stat_name)
if callable(stat_func):
setattr(MyObjectSet, stat_name, lambda S: sum(stat_func(p) for p in S))
if __name__ == "__main__":
S = MyObjectSet(MyObject(t) for t in [(1,2), (3,4)])
result, expected = S.stat_1(), sum(p.stat_1() for p in S)
print(f"S.size() = {result}, expected {expected}")
result, expected = S.stat_2(), sum(p.stat_2() for p in S)
print(f"S.sum() = {result}, expected {expected}")
Is there any way to achieve this functionality?
replace your lambda with this:
lambda S, f=stat_func: sum(f(p) for p in S)
It copies the stat_func into f, instead of capturing a reference to it, which was what happened in your original code (so all stat_funcs inside your different lambdas ended up being the last value assigned to the stat_func in the for loop.
You can simply override __getattr__ to treat any possible method call as a summing wrapper around the object's method of the same name. This simple example will just raise an AttributeError if the underlying method doesn't exist; you may want to catch the exception and raise another error of your own.
class MyObjectSet(set):
def __getattr__(self, mn):
return lambda: sum(methodcaller(mn)(x) for x in self)
Related
This question already has answers here:
Creating functions (or lambdas) in a loop (or comprehension)
(6 answers)
Closed 6 months ago.
I want to add properties dynamically to my class as follows, however I end up creating aliases. How can I prevent this?
class A:
def __init__(self, a, b):
self._a = a
self._b = b
for attr in ('a', 'b'):
f = lambda self: getattr(self, '_'+attr)
setattr(A, attr, property(f, None))
a = A(0,1)
print(a.a)
print(a.b)
However this yields:
1
1
Edit:
The comment on closure scoping is relevant, however that leaves the question whether one can generate properties dynamically that reference some attribute of self open.
Specifically with respect to the example above: how, if at all, can I set the property such that a.a returns 0 instead of 1? If I simply try to pass the attribute argument to the lambda function, this attribute will need to be passed and thus this won't work.
In order to get your desired result, you'd need to wrap your lambda in another function, like so:
def make_fget(attr):
return lambda self: getattr(self, '_' + attr)
This way, when you call make_fget, the local name attr is bound to the argument passed.
You can call this in a loop as in your original code:
for attr in ('a', 'b'):
setattr(A, attr, property(make_fget(attr), None))
The difference here is that in your original version, the loop essentially reassigns attr every iteration, and the lambda is only looking at attr in the outer scope (the loop) at the time it is called, and it will end up with whatever was assigned in the last iteration.
By wrapping in another function, in every loop iteration you essentially create a fresh outer scope (the function call), with the name attr bound to the passed argument, for the returned lambda.
In python, is it possible to chain together class methods and functions together? For example, if I want to instantiate a class object and call a method on it that affects an instance variable's state, could I do that? Here is an example:
class Test(object):
def __init__(self):
self.x = 'Hello'
#classmethod
def make_upper(y):
y.x = y.x.upper()
What I'm wanting to do is this:
h = Test().make_upper()
I want to instantiate a class object and affect the state of a variable in one line of code, but I would also like to be able to chain together multiple functions that can affect state or do something else on the object. Is this possible in python like it is in jQuery?
Yes, sure. Just return self from the instance methods you are interested in:
class Test(object):
def __init__(self):
self.x = 'Hello'
def make_upper(self):
self.x = self.x.upper()
return self
def make_lower(self):
self.x = self.x.lower()
return self
h = Test().make_upper()
print(h.x)
Output:
HELLO
Yes and no. The chaining certainly works, but h is the return value of make_upper(), not the object returned by Test(). You need to write this as two lines.
h = Test()
h.make_upper()
However, PEP-572 was recently accepted for inclusion in Python 3.8, which means someday you could write
(h := Test()).make_upper()
The return value of Test() is assigned to h in the current scope and used as the value of the := expression, which then invokes its make_upper method. I'm not sure I would recommend using := in this case, though; the currently required syntax is much more readable.
I have a class (list of dicts) and I want it to sort itself:
class Table(list):
…
def sort (self, in_col_name):
self = Table(sorted(self, key=lambda x: x[in_col_name]))
but it doesn't work at all. Why? How to avoid it? Except for sorting it externally, like:
new_table = Table(sorted(old_table, key=lambda x: x['col_name'])
Isn't it possible to manipulate the object itself? It's more meaningful to have:
class Table(list):
pass
than:
class Table(object):
l = []
…
def sort (self, in_col_name):
self.l = sorted(self.l, key=lambda x: x[in_col_name])
which, I think, works.
And in general, isn't there any way in Python which an object is able to change itself (not only an instance variable)?
You can't re-assign to self from within a method and expect it to change external references to the object.
self is just an argument that is passed to your function. It's a name that points to the instance the method was called on. "Assigning to self" is equivalent to:
def fn(a):
a = 2
a = 1
fn(a)
# a is still equal to 1
Assigning to self changes what the self name points to (from one Table instance to a new Table instance here). But that's it. It just changes the name (in the scope of your method), and does affect not the underlying object, nor other names (references) that point to it.
Just sort in place using list.sort:
def sort(self, in_col_name):
super(Table, self).sort(key=lambda x: x[in_col_name])
Python is pass by value, always. This means that assigning to a parameter will never have an effect on the outside of the function. self is just the name you chose for one of the parameters.
I was intrigued by this question because I had never thought about this. I looked for the list.sort code, to see how it's done there, but apparently it's in C. I think I see where you're getting at; what if there is no super method to invoke? Then you can do something like this:
class Table(list):
def pop_n(self, n):
for _ in range(n):
self.pop()
>>> a = Table(range(10))
>>> a.pop_n(3)
>>> print a
[0, 1, 2, 3, 4, 5, 6]
You can call self's methods, do index assignments to self and whatever else is implemented in its class (or that you implement yourself).
This question already has answers here:
Calling a function of a module by using its name (a string)
(18 answers)
Closed 5 years ago.
How to construct and call a function from a string?
For example, consider three different functions and a list of strings, I want to be able to use the items in list of strings to construct and call the appropriate function
def do_function1():
return 'done function1'
def do_function2():
return 'done function2'
def do_function3():
return 'done function3'
listOfstr = ['function1','function2','function3']
for item in listOfstr:
result = 'do_'+item()
print(result)
result = 'do_'+item()
TypeError: 'str' object is not callable
The problematic code is
listOfstr = ['function1','function2','function3']
for item in listOfstr:
result = 'do_'+item()
In the first loop item, will have the value 'function1'. You are calling this string as if it were a function. But strings are not callable and have no code assigned to them!
Then, you go on with the for loop before doing anything.
Simply refer to item, like this:
for item in listOfstr:
func_name = 'do_' + item
func = globals()[func_name]
func()
The most explicit way would be to have a dictionary of those functions:
funcs = {
'function1': do_function1,
'function2': do_function2,
'function3': do_function3,
}
funcs[item]()
That way you can also name your functions whatever you want, decouple from item names, make them methods, move to other modules etc without breaking the general design. The other way is globals, as already answered.
First of all, usually you won't need this. Instead of putting strings in a list, you can also put functions themselves in a list like this:
def do_function1():
return 'done function1'
def do_function2():
return 'done function2'
def do_function3():
return 'done function3'
list_of_functions = [do_function1, do_function2, do_function3]
for item in list_of_functions:
result = item()
print(result)
However, if you insist, you can do it like this:
locals()["do_function1"]()
locals() gives you a dictionary of locally defined objects by name.
On Codewars.com I encountered the following task:
Create a function add that adds numbers together when called in succession. So add(1) should return 1, add(1)(2) should return 1+2, ...
While I'm familiar with the basics of Python, I've never encountered a function that is able to be called in such succession, i.e. a function f(x) that can be called as f(x)(y)(z).... Thus far, I'm not even sure how to interpret this notation.
As a mathematician, I'd suspect that f(x)(y) is a function that assigns to every x a function g_{x} and then returns g_{x}(y) and likewise for f(x)(y)(z).
Should this interpretation be correct, Python would allow me to dynamically create functions which seems very interesting to me. I've searched the web for the past hour, but wasn't able to find a lead in the right direction. Since I don't know how this programming concept is called, however, this may not be too surprising.
How do you call this concept and where can I read more about it?
I don't know whether this is function chaining as much as it's callable chaining, but, since functions are callables I guess there's no harm done. Either way, there's two ways I can think of doing this:
Sub-classing int and defining __call__:
The first way would be with a custom int subclass that defines __call__ which returns a new instance of itself with the updated value:
class CustomInt(int):
def __call__(self, v):
return CustomInt(self + v)
Function add can now be defined to return a CustomInt instance, which, as a callable that returns an updated value of itself, can be called in succession:
>>> def add(v):
... return CustomInt(v)
>>> add(1)
1
>>> add(1)(2)
3
>>> add(1)(2)(3)(44) # and so on..
50
In addition, as an int subclass, the returned value retains the __repr__ and __str__ behavior of ints. For more complex operations though, you should define other dunders appropriately.
As #Caridorc noted in a comment, add could also be simply written as:
add = CustomInt
Renaming the class to add instead of CustomInt also works similarly.
Define a closure, requires extra call to yield value:
The only other way I can think of involves a nested function that requires an extra empty argument call in order to return the result. I'm not using nonlocal and opt for attaching attributes to the function objects to make it portable between Pythons:
def add(v):
def _inner_adder(val=None):
"""
if val is None we return _inner_adder.v
else we increment and return ourselves
"""
if val is None:
return _inner_adder.v
_inner_adder.v += val
return _inner_adder
_inner_adder.v = v # save value
return _inner_adder
This continuously returns itself (_inner_adder) which, if a val is supplied, increments it (_inner_adder += val) and if not, returns the value as it is. Like I mentioned, it requires an extra () call in order to return the incremented value:
>>> add(1)(2)()
3
>>> add(1)(2)(3)() # and so on..
6
You can hate me, but here is a one-liner :)
add = lambda v: type("", (int,), {"__call__": lambda self, v: self.__class__(self + v)})(v)
Edit: Ok, how this works? The code is identical to answer of #Jim, but everything happens on a single line.
type can be used to construct new types: type(name, bases, dict) -> a new type. For name we provide empty string, as name is not really needed in this case. For bases (tuple) we provide an (int,), which is identical to inheriting int. dict are the class attributes, where we attach the __call__ lambda.
self.__class__(self + v) is identical to return CustomInt(self + v)
The new type is constructed and returned within the outer lambda.
If you want to define a function to be called multiple times, first you need to return a callable object each time (for example a function) otherwise you have to create your own object by defining a __call__ attribute, in order for it to be callable.
The next point is that you need to preserve all the arguments, which in this case means you might want to use Coroutines or a recursive function. But note that Coroutines are much more optimized/flexible than recursive functions, specially for such tasks.
Here is a sample function using Coroutines, that preserves the latest state of itself. Note that it can't be called multiple times since the return value is an integer which is not callable, but you might think about turning this into your expected object ;-).
def add():
current = yield
while True:
value = yield current
current = value + current
it = add()
next(it)
print(it.send(10))
print(it.send(2))
print(it.send(4))
10
12
16
Simply:
class add(int):
def __call__(self, n):
return add(self + n)
If you are willing to accept an additional () in order to retrieve the result you can use functools.partial:
from functools import partial
def add(*args, result=0):
return partial(add, result=sum(args)+result) if args else result
For example:
>>> add(1)
functools.partial(<function add at 0x7ffbcf3ff430>, result=1)
>>> add(1)(2)
functools.partial(<function add at 0x7ffbcf3ff430>, result=3)
>>> add(1)(2)()
3
This also allows specifying multiple numbers at once:
>>> add(1, 2, 3)(4, 5)(6)()
21
If you want to restrict it to a single number you can do the following:
def add(x=None, *, result=0):
return partial(add, result=x+result) if x is not None else result
If you want add(x)(y)(z) to readily return the result and be further callable then sub-classing int is the way to go.
The pythonic way to do this would be to use dynamic arguments:
def add(*args):
return sum(args)
This is not the answer you're looking for, and you may know this, but I thought I would give it anyway because if someone was wondering about doing this not out of curiosity but for work. They should probably have the "right thing to do" answer.