Calling class function saved as attribute - python

I have two classes and a "use" class function that performs multiple actions using the various attributes as the inputs so that I can just call the one function and get different results based on which class object is referenced. Where I am getting stuck is that I want to have part of the use function check the 'effect' attribute and call the function found there. Currently, the function named in effect is called when the object c is defined, and everything I have tried within the use function has no effect or returns 'none' since I don't have a return statement in the add and sub functions.
I've provided a simplified example code below. C has 9 attributes and 10 different class functions that I would want to use in the effect spot. I plan on having 50+ different C objects, so not having to write out specific functions for each one would be spectacular.
In this example, the print(p.h) at the end returns 101, showing that designing the C object calls the add function I put in the attribute:
M= []
class P:
def __init__(p, h, s):
p.h= h
p.s=s
class C:
def __init__(y, name, d, f effect,):
y.name= name
y.effect= effect
y.d= d
y.f= f
def use(c):
M.append(c)
p.h -= p. y.d
p.s += y.f
effect
def add(c, x):
p.h += x
def sub(c, x):
p.h -=x
p= P(100)
c= C('test1', add(1), 1)
print(p.h)
I have tried the add and sub functions as both class and standalone, which didn't seem to make a difference, calling y.effect as though it were a function which just returns 'none' as mentioned, and adding the property decorator, which threw an error, probably because I don't quite understand what that is supposed to do yet.

When accessing methods of classes C and P, you will only be able to access them from the class namespace. C.add will work, but add will not.
Furthermore if you call c.add(4) it will be the same thing as calling C.add(c, 4) because it will implicitly pass the instance c into the class (C) method C.add. You cannot call add(1) or c.add(1) before your instance c is initialized by the __init__ method.
Typically when writing python, it is most clear to always name the first argument to your class method self to make it clear that self refers to the instance and that it will automatically get passed into the function.
Furthermore, because you do not instantiate p until after you define your class C, you won't be able to access p from inside your C class unless you pass p into the function or save it as an attribute of c.
Not totally sure what you are going for here but I made some modifications which might help.
#!/usr/bin/env python
M = []
class P:
def __init__(self, h, s):
# self refers to your instance of p
self.h = h
self.s = s
class C:
def __init__(self, name, p, d, f, effect):
# self refers to your instance of c
self.p = p # save an instance of p as an attribute of your instance of c
self.name = name
self.effect = effect
self.d = d
self.f = f
def use(self):
M.append(self)
self.p.h -= self.d
self.p.s += self.f
return self.effect
def add(self, x):
# self refers to your instance of c and gets implicitly passed in as the first argument
# when c.add(2) is called
self.p.h += x
def sub(self, x):
self.p.h -= x
p = P(100, 50)
c = C('test1', p, d=2, f=1, effect="a")
c.add(1)
print(p.h)
<script src="https://modularizer.github.io/pyprez/pyprez.min.js"></script>

This code isn't runnable as-is, so there may be other problems with your real code that aren't possible to discern from this example, but when you say:
function named in effect is called when the object c is defined
that's happening not because of what's inside your C class, but the line where you construct your C:
c= C('test1', add(1), 1)
The expression add(1) isn't passing the add function, it's calling the add function and passing its result. To pass a function as an argument, just pass the function itself without the ():
c = C('test1', add, 1)
Note that in the code you provided there is no function called add in the current scope, and your C.use does not call effect, which is a different problem.

Related

Change only certain arguments in a class/method, hold others constant

I have a class & method, each with several arguments: my_class(a,b,c).my_method(d,e,f) and I'd like to be able to only change a single argument, while holding the others constant.
Constantly copy-pasting the other constant arguments seems bad, so I'd like to create a new object wrapper_fct where I reference my_class but only provide the one argument I want to change, b, without always having to specify the remaining arguments. How would wrapper_fct() look like?
For example, wrapper_fct(my_class, b1) would return my_class(a,b1,c).my_method(d,e,f), wrapper_fct(my_class, b2) would return my_class(a,b2,c).my_method(d,e,f).
Here's an example in practice:
Loop through just the variable b and evaluate several classes/methods for each new instance of b, and append the results in a list.
I can currently do this in a for loop:
mylist1 = [] # init lists (append results here)
mylist2 = []
mylist2 = []
for b in [1,2,3,4,5]:
mylist1.append( my_class1(a,b,c).my_method(d,e,f) )
mylist2.append( my_class2(a,b,c).my_method(d,e,f) )
mylist3.append( my_class3(a,b,c).my_method(d,e,f) )
...
But it seems better to create a function loop_through_B() and use the wrapper_fct(my_class,b) as specified above. Not sure if it's the ideal solution, but maybe something like:
def loop_through_B(input_class, b_values = [1,2,3,4,5])
mylist = []
for b in b_values:
mylist.append( wrapper_fct(input_class,b) )
return mylist
loop_through_B(my_class1) # would I also have to specify the method here as well?
loop_through_B(my_class2)
loop_through_B(my_class3)
Extra Question: how would I add the ability to vary method arguments, or even multiple class & method arguments?
After #chepner pointed me in the right direction, I think the best solution is to use the lambda function:
wrapper_fct = lambda b: my_class1(a,b,c).my_method(d,e,f)
In this case, I can vary b as much as I want while holding the class arguments a,c, and method arguments d,e,f constant. Note that with lambda functions, I can also vary the method arguments and/or the class arguments. For example:
wrapper_fct_multiple = lambda b, e: my_class1(a,b,c).my_method(d,e,f)
It is also possible to do this with functools.partial, but it's not obvious to me how I would specify both class & method arguments with functools.
Anyway, here is the solution implementation using lambda:
# define the "wrapper function" outside the loop
wrapper_fct = lambda b: my_class1(a,b,c).my_method(d,e,f)
# define the function I want to use to loop through B:
def loop_through_B(class_wrapper, b_values)
mylist = []
for b in b_values:
mylist.append( class_wrapper(b) )
return mylist
# run:
loop_through_B(wrapper_fct, b_values=[1,2,3,4,5])
# Can make additional wrapper_fct2, wrapper_fct3, for my_class2, my_class3 ...
You can pass the method a dictionary of arguments, and change what the method sees by selectively updating it when calling the method.
Here's what I mean:
class MyClass:
def __init__(self, a, b, c):
self.a, self.b, self.c = a, b, c
def my_method(self, kwargs):
return sum((kwargs[key] for key in kwargs.keys()))
def __repr__(self):
classname = type(self).__name__
args = ', '.join((f'{v!r}' for v in (self.a, self.b, self.c)))
return f'{classname}({args})'
instance = MyClass('a','b','c')
print(instance) # -> MyClass('a', 'b', 'c')
kwargs = dict(d=1, e=2, f=3)
print(instance.my_method(kwargs)) # -> 6
print(instance.my_method(dict(kwargs, e=38))) # -> 42

Python: Passing an instance to a nested function

class A:
def __init__(self, x, y):
self.base = x
self.core = y
def addBaseCore(self, initial):
def addBase(z): # vs. def addBase(self, z)
return z + self.base
initial += self.core
# vs. Do we pass self to the nested function? e.g. addBase(self, z)
return addBase(initial)
I am new to Python and I am curious how/why is it possible to access the attributes of an instance within a nested function without explicitly passing the instance to the nested function (as shown above)?
The inner method is not exactly an instance method neither does it receive the instance as an argument. How is the inner method still able to access the attributes of the instance?
Thank you for all the help.
There's no need to pass self into the nested function that's defined inside your member function.
Here's an example using your code.
a = A(10, 20)
print(a.addBaseCore(30))
Output
60

unable to use attribute from a python function in another python script

I want to use one of the attributes returned by a function in a python script (x) into a python script (y)
The communication between both scripts works well; I can get functions and attributes, but doesn't allow me to attributes returned by a function.
Here is how I worked:
x.py
def func():
b = 10
a = 15
return [a,b]
c = 20
y.py
from x import func
import x
print (x.c)
print (func.b)
I get the "c" value and the following error AttributeError: 'function' object has no attribute 'b'
I have tried also to print x.b, and I've got AttributeError: module 'WorkingLSTM' has no attribute 'b'
Thanks in advance
The way to call func is by using func(), which would give you [a,b].
example:
funcResult = func()
a = funcResult[0]
b = funcResult[1]
funcResults is the return value from func(), that is the list [a,b].
That's not allowed, you have to call the function to get the value from the functions returned list.
a, b = func()
print(b)
# or just...
print(func()[1])
PS: It's "not allowed" because it doesn't make sense in any way; when there is no function call, there is not variable b at all. You might take a look at classes, they can hold static variables.
you cannot access local variables of a function.
these variables exist only during the the time where func is executed and are destroyed afterwards.
You can of course call the function and look at the result, but the result is just a list with two values
rslt = func()
print("A = ", rslt[0])
print("B = ", rslt[1])
The variable was declared inside a function making it a local variable and as such it can"t be accessed outside the function.
The variable is declared outside of the function making it a global variable and is not in anyway tied to your function.
The concept of attributes relates to Classes and you are dealing with a function so you might have to treat it as a class object.
If you are concerned bout accessing the local variables, you might as well do this:
y.py
from x import *
d = func() # func returns a list which is now
# identified/referenced by variable d
# displays the elements in the list using index position
print(d[0])
print(d[1])
If you want to use attributes, you may create a callable class instead of function:
class Func:
def __init__(self):
self.b = 10
self.a = 15
def __call__():
return [self.a, self.b]
func = Func()
Python has the concept of the scope. Local variables have no effect outside the function.
If you want to use it, use class and self or make getter function(but it's not Pythonic).
x.py
class X:
def __init__(self):
self.b = 10
self.a = 15
self.c = 20
def func(self):
return [self.a, self.b]
y.py
from x import X
x = X()
print(x.c)
print(x.func()[1])

Is self variable computed multiple times if functions are called more than once in Python?

I have a class where the shared variable self.a is obtained after a very heavy computation which requires a lot of time:
class MyClass(object):
def __init__(self):
# ----------------------------------
# function computationally demanding
out = demanding_function() # In this example, the output is a list of characters ['A','X','R','N','L']
# ----------------------------------
self.a = out
def fun1(self, string):
out = []
for letter in self.a:
out.append(string+letter)
return out
def fun2(self, number):
out = []
for letter in self.a:
out.append(str(number)+letter)
return out
o = MyClass()
x = o.fun1('Hello ')
y = o.fun2(2)
As you can see, self.a is used by the functions fun1 and fun2.
Here is my question is the following: if I call those 2 functions, is the demanding_function() executed multiple times or just once?
Note: this is a generic example and the variables don't have any specific meaning
The function is called just once, when the class instance is initialised i.e. when the __init__ of the class is called. Every other time you access self.a, the already assigned value is used; so no worries.
__init__ is only called once, when you instantiate the object. Any subsequent method calls using that instantiated object will use the already-computed values of instance varaibles

In Python, can I bind a variable to a function/expression so that it automatically updates?

Let's say I've got a variable A that is the result of a function/expression F. F in it's turn has a number of other variables in it, let's say X,Y and Z.
Is it possible to bind A to F so that whenever X,Y or Z changes, A will be updated automatically?
What I want to avoid is that everytime X,Y and Z changes, I have to remember to update A explicitly in the code. I also don't want to call the function everytime I want to use the A.
Example (as per requested): I've got the following function:
def calcHits():
return sum(hitDiceRolls,level*modList['con'])
and in my program (outside of the function), I've got a variable called hitPoints (yes, it's a roleplaying game program). Whenever the variables that's used in the function is changed, I want hitPoints to change as well.
The typical way to do this in Python would be to use a class:
class ExpressionBinder:
def __init__(self, f):
self.f = f
self.x = 0
self.y = 0
self.z = 0
#property
def result(self):
return self.f(self.x, self.y, self.z)
You can use it like this:
def f(x, y, z):
return x**3 + y**2 + z
b = ExpressionBinder(f)
b.x = 1
b.y = 2
b.z = 3
print(b.result)
There is no way in Python to automatically rebind a name in global or local scope in response to other names being rebound. However, it should be possible to make a class that can keep track of some values and have a member function that returns the value you called A. And, as #Alok pointed out, you can use property descriptors to make a member name that implicitly calls a function to return its value, so you can hide the function and treat the name like a plain old name.
class Trk(object):
"""Track some values and compute a function if any change"""
def __init__(self, name, fn, **objects_to_track):
def _trk_fn(self):
if any(self.__dict__[x] != self.original_objects[x] for x in self.original_objects):
self.value = self.saved_fn(self.__dict___)
# now that self.value is updated, also update self.original_objects
for x in self.original_objects:
self.original_objects[x] = self.__dict__[x]
return self.value
self.original_objects = objects_to_track # make reference copy
self.__dict__.update(objects_to_track)
self.name = name
self.saved_fn = fn
self.fn = self._trk_fn()
self.value = self.fn()
I'm sorry but I am very tired right now, and I canot finish this example. I didn't test it either. But this shows one way to track values, and if they are different, do something different. You use it like this:
# want to track x, y, z
trk = Trk(x, y, z)
trk.fn() # returns up-to-date value
trk.x = new_value
trk.fn() #detects that trk.x changed and computes new trk.value
If the above works, you can use the property descriptor stuff to bind a name such that an attempt to read a value from the name will call self.fn()
EDIT: Oh, it's important that when self.value is updated, self.original_objects should be updated. I've added code to do that.
And now I'm going to sleep!

Categories

Resources