(Based on this question):
One can override the __setattr__ magic method for an object to have additional instructions when an attribute of an object is set. As in:
class MyClass(object):
def __init__(self, attribute=None):
object.__init__(self)
self.attribute = attribute
def __setattr__(self, name, value):
self.__dict__[name] = value
if name == 'attribute':
print("attribute's value is modified to {}.".format(
self.attribute))
if __name__ == '__main__':
my_obj = MyClass(True)
while True:
my_obj.attribute = input()
How can I catch a particular name assignment in the current script
without using classes(specifically to call a method with more
instructions)?
def b_is_modified():
print("b is modified!")
if __name__ == '__main__':
a = 3
b = 4
b = 5
How to call b_is_modified when b is assigned a value?
I think the other answer by Nae sums it up; I'm not aware of any built-in mechanisms in the Python languages to detect assignments, so if you want an interrupt-like event system to trigger upon assignment I don't know if it's feasible.
However, you seem quiet determined to get a way to "detect" assignment, so I want to describe an approach that might get you closer than nothing.
There are the built-in functions globals() and locals() that creates dictionary of variables in global and local scope respectively. (They, in additon to vars() are further explained here).
A noteworthy point is that locals() will behave differently if called from inside a function:
If locals() is called inside a function it constructs a dictionary of the function namespace as of that moment and returns it -- any further name assignments are not reflected in the returned dictionary, and any assignments to the dictionary are not reflected in the actual local namespace
If locals() is called outside a function it returns the actual dictionary that is the current namespace. Further changes to the namespace are reflected in the dictionary, and changes to the dictionary are reflected in the namespace:
Here is a "hacky" way to detect changes to variables:
def b_is_modified():
print("b is modified!")
if __name__ == '__main__':
old = locals().get('b')
a = 3
b = 4
b = 5
new = locals().get('b')
if id(new) != id(old) and new is not None:
b_is_modified()
This is nothing else but an (obfuscated?) way of checking if the value of b has changed from one point in execution to another, and there is no callback event or trigger action that detects it. However, if you want to expand on this approach continue reading.
The rest of the answer explains how to check for changes in b by rewriting it to something like:
if __name__ == '__main__':
monitor = ScopeVariableMonitor(locals())
a = 3
b = 4
monitor.compare_and_update() # Detects creation of a and b
b = 5
monitor.compare_and_update() # Detects changes to b
The following will "detect" any changes to the variables, and I've also included an example where it's used inside a function, to reiterate that then the dictionary returned from locals() does not update.
The ScopeVariableMonitor-class is just an example, and combines the code in one place. In essence, it's comparing changes to the existence and values of variables between update()s.
class ScopeVariableMonitor:
def __init__(self, scope_vars):
self.scope_vars = scope_vars # Save a locals()-dictionary instance
self.old = self.scope_vars.copy() # Make a shallow copy for later comparison
def update(self, scope_vars=None):
scope_vars = scope_vars or self.scope_vars
self.old = scope_vars.copy() # Make new shallow copy for next time
def has_changed(self, var_name):
old, new = self.old.get(var_name), self.scope_vars.get(var_name)
print('{} has changed: {}'.format(var_name, id(old) != id(new)))
def compare_and_update(self, var_list=None, scope_vars=None):
scope_vars = scope_vars or self.scope_vars
# Find new keys in the locals()-dictionary
new_variables = set(scope_vars.keys()).difference(set(self.old.keys()))
if var_list:
new_variables = [v for v in new_variables if v in var_list]
if new_variables:
print('\nNew variables:')
for new_variable in new_variables:
print(' {} = {}'.format(new_variable, scope_vars[new_variable]))
# Find changes of values in the locals()-dictionary (does not handle deleted vars)
changed_variables = [var_name for (var_name, value) in self.old.items() if
id(value) != id(scope_vars[var_name])]
if var_list:
changed_variables = [v for v in changed_variables if v in var_list]
if changed_variables:
print('\nChanged variables:')
for var in changed_variables:
print(' Before: {} = {}'.format(var, self.old[var]))
print(' Current: {} = {}\n'.format(var, scope_vars[var], self.old[var]))
self.update()
The "interesting" part is the compare_and_update()-method, if provided with a list of variables names, e.g. ['a', 'b'], it will only look for changes to those to variables. The scope_vars-parameter is required when inside the function scope, but not in the global scope; for reasons explained above.
def some_function_scope():
print('\n --- Now inside function scope --- \n')
monitor = ScopeVariableMonitor(locals())
a = 'foo'
b = 42
monitor.compare_and_update(['a', 'b'], scope_vars=locals())
b = 'bar'
monitor.compare_and_update(scope_vars=locals())
if __name__ == '__main__':
monitor = ScopeVariableMonitor(locals())
var_list = ['a', 'b']
a = 5
b = 10
c = 15
monitor.compare_and_update(var_list=var_list)
print('\n *** *** *** \n') # Separator for print output
a = 10
b = 42
c = 100
d = 1000
monitor.has_changed('b')
monitor.compare_and_update()
some_function_scope()
Output:
New variables:
a = 5
b = 10
*** *** ***
b has changed: True
New variables:
d = 1000
Changed variables:
Before: b = 10
Current: b = 42
Before: a = 5
Current: a = 10
Before: c = 15
Current: c = 100
--- Now inside function scope ---
New variables:
a = foo
b = 42
Changed variables:
Before: b = 42
Current: b = bar
Conclusion
My answer is just a more general way of doing:
b = 1
old_b = b
# ...
if b != old_b:
print('b has been assigned to')
The dictionary from locals() will hold everything that is a variable, including functions and classes; not just "simple" variables like your a, b and c.
In the implementation above, checks between "old" and "new" values are done by comparing the id() of the shallow copy of before with id() of the current value. This approach allows for comparison of ANY value, because the id() will return the virtual memory address, but this is assumable far from a good, general comparison scheme.
I'm curious to what you want to achieve and why you want to detect assignments: if you share your goal then perhaps I can think of another way to reach it in another way.
Based on this answer:
It can't be catched(at least in python level).
Simple name assignment(b = 4), as oppposed to object attribute assignment (object.b = 5), is a fundamental operation of the language itself. It's not implemented in terms of a lower-level operation that one can override. Assignment just is.
Related
Consider this example:
def A():
b = 1
def B():
# I can access 'b' from here.
print(b)
# But can i modify 'b' here?
B()
A()
For the code in the B function, the variable b is in a non-global, enclosing (outer) scope. How can I modify b from within B? I get an UnboundLocalError if I try it directly, and using global does not fix the problem since b is not global.
Python implements lexical, not dynamic scope - like almost all modern languages. The techniques here will not allow access to the caller's variables - unless the caller also happens to be an enclosing function - because the caller is not in scope. For more on this problem, see How can I access variables from the caller, even if it isn't an enclosing scope (i.e., implement dynamic scoping)?.
On Python 3, use the nonlocal keyword:
The nonlocal statement causes the listed identifiers to refer to previously bound variables in the nearest enclosing scope excluding globals. This is important because the default behavior for binding is to search the local namespace first. The statement allows encapsulated code to rebind variables outside of the local scope besides the global (module) scope.
def foo():
a = 1
def bar():
nonlocal a
a = 2
bar()
print(a) # Output: 2
On Python 2, use a mutable object (like a list, or dict) and mutate the value instead of reassigning a variable:
def foo():
a = []
def bar():
a.append(1)
bar()
bar()
print a
foo()
Outputs:
[1, 1]
You can use an empty class to hold a temporary scope. It's like the mutable but a bit prettier.
def outer_fn():
class FnScope:
b = 5
c = 6
def inner_fn():
FnScope.b += 1
FnScope.c += FnScope.b
inner_fn()
inner_fn()
inner_fn()
This yields the following interactive output:
>>> outer_fn()
8 27
>>> fs = FnScope()
NameError: name 'FnScope' is not defined
I'm a little new to Python, but I've read a bit about this. I believe the best you're going to get is similar to the Java work-around, which is to wrap your outer variable in a list.
def A():
b = [1]
def B():
b[0] = 2
B()
print(b[0])
# The output is '2'
Edit: I guess this was probably true before Python 3. Looks like nonlocal is your answer.
No you cannot, at least in this way.
Because the "set operation" will create a new name in the current scope, which covers the outer one.
I don't know if there is an attribute of a function that gives the __dict__ of the outer space of the function when this outer space isn't the global space == the module, which is the case when the function is a nested function, in Python 3.
But in Python 2, as far as I know, there isn't such an attribute.
So the only possibilities to do what you want is:
1) using a mutable object, as said by others
2)
def A() :
b = 1
print 'b before B() ==', b
def B() :
b = 10
print 'b ==', b
return b
b = B()
print 'b after B() ==', b
A()
result
b before B() == 1
b == 10
b after B() == 10
.
Nota
The solution of Cédric Julien has a drawback:
def A() :
global b # N1
b = 1
print ' b in function B before executing C() :', b
def B() :
global b # N2
print ' b in function B before assigning b = 2 :', b
b = 2
print ' b in function B after assigning b = 2 :', b
B()
print ' b in function A , after execution of B()', b
b = 450
print 'global b , before execution of A() :', b
A()
print 'global b , after execution of A() :', b
result
global b , before execution of A() : 450
b in function B before executing B() : 1
b in function B before assigning b = 2 : 1
b in function B after assigning b = 2 : 2
b in function A , after execution of B() 2
global b , after execution of A() : 2
The global b after execution of A() has been modified and it may be not whished so
That's the case only if there is an object with identifier b in the global namespace
The short answer that will just work automagically
I created a python library for solving this specific problem. It is released under the unlisence so use it however you wish. You can install it with pip install seapie or check out the home page here https://github.com/hirsimaki-markus/SEAPIE
user#pc:home$ pip install seapie
from seapie import Seapie as seapie
def A():
b = 1
def B():
seapie(1, "b=2")
print(b)
B()
A()
outputs
2
the arguments have following meaning:
The first argument is execution scope. 0 would mean local B(), 1 means parent A() and 2 would mean grandparent <module> aka global
The second argument is a string or code object you want to execute in the given scope
You can also call it without arguments for interactive shell inside your program
The long answer
This is more complicated. Seapie works by editing the frames in call stack using CPython api. CPython is the de facto standard so most people don't have to worry about it.
The magic words you are probably most likely interesed in if you are reading this are the following:
frame = sys._getframe(1) # 1 stands for previous frame
parent_locals = frame.f_locals # true dictionary of parent locals
parent_globals = frame.f_globals # true dictionary of parent globals
exec(codeblock, parent_globals, parent_locals)
ctypes.pythonapi.PyFrame_LocalsToFast(ctypes.py_object(frame),ctypes.c_int(1))
# the magic value 1 stands for ability to introduce new variables. 0 for update-only
The latter will force updates to pass into local scope. local scopes are however optimized differently than global scope so intoducing new objects has some problems when you try to call them directly if they are not initialized in any way. I will copy few ways to circumvent these problems from the github page
Assingn, import and define your objects beforehand
Assingn placeholder to your objects beforehand
Reassign object to itself in main program to update symbol table: x = locals()["x"]
Use exec() in main program instead of directly calling to avoid optimization. Instead of calling x do: exec("x")
If you are feeling that using exec() is not something you want to go with you can
emulate the behaviour by updating the the true local dictionary (not the one returned by locals()). I will copy an example from https://faster-cpython.readthedocs.io/mutable.html
import sys
import ctypes
def hack():
# Get the frame object of the caller
frame = sys._getframe(1)
frame.f_locals['x'] = "hack!"
# Force an update of locals array from locals dict
ctypes.pythonapi.PyFrame_LocalsToFast(ctypes.py_object(frame),
ctypes.c_int(0))
def func():
x = 1
hack()
print(x)
func()
Output:
hack!
I don't think you should want to do this. Functions that can alter things in their enclosing context are dangerous, as that context may be written without the knowledge of the function.
You could make it explicit, either by making B a public method and C a private method in a class (the best way probably); or by using a mutable type such as a list and passing it explicitly to C:
def A():
x = [0]
def B(var):
var[0] = 1
B(x)
print x
A()
For anyone looking at this much later on a safer but heavier workaround is. Without a need to pass variables as parameters.
def outer():
a = [1]
def inner(a=a):
a[0] += 1
inner()
return a[0]
You can, but you'll have to use the global statment (not a really good solution as always when using global variables, but it works):
def A():
global b
b = 1
def B():
global b
print( b )
b = 2
B()
A()
Consider this (uncontroversial) simple example:
allvars = []
a = 1
allvars.append(a)
b = 2
allvars.append(b)
c = 3
allvars.append(c)
d = 4
allvars.append(d)
e = 5
allvars.append(e)
for ix in allvars:
ix = ix + 1 # changes local variable ix, but not array elements
print(allvars) # [1, 2, 3, 4, 5]
for i, ix in enumerate(allvars):
allvars[i] = ix + 1 # changes array elements ...
print(allvars) # [2, 3, 4, 5, 6]
# ... but not original variables
print(a,b,c,d,e) # 1 2 3 4 5
Even if we had some variables "stored" into a Python list - changing that list did not change the original variables.
It is clear why this happens, if we recall that Python in fact stores pointers (as I read somewhere, "python has names, not variables"):
when we do a = 1; a points to the address of the int object 1
allvars[0], which is where we thought we stored a, also gets the address of the int object 1
In allvars[0] = allvars[0]+1, the final allvars[0] gets the address of the resulting int object, 2
however, that doesn't change the fact that a still points to the int object 1
The thing is, however, - I have a situation, where I have to manage a bunch of variables (like a, b ... above) separately; however, in the code, there are cases that would be more straightforward to handle, if I ran a loop over all these variables - but, the variables would have to be updated, because after the phase when the loops are useful, I have some remaining processing to be done on the variables (a, b etc) individually, where the updated values are expected.
So is there some (not too convoluted) way in Python, to put variables (or maybe, variable names) in a list/array, and then iterate over that array - and change the original variable (names)?
In terms of above example, I'd want to do something like this pseudocode:
...
for i, ix in enumerate(allvars):
change_originals("allvars[i] = ix + 1")
print(a,b,c,d,e) # this should be 2, 3, 4, 5, 6
Here you have created an array of primitives value. Primitives always copy whenever you use it. So mofication wont reflect on the original variable.
There are possible solution base on your requirement.
class Val:
def __init__(self, val = -1):
self._val = val
def get_val(self):
return self._val
# setter method
def set_val(self, x):
self._val = x
allvars = []
one = Val(1)
allvars.append(one)
print(allvars[0]._val)
one.set_val(2)
print(allvars[0]._val)
You can use a dictionary with key[1,2,3,4...]
You can create array of object
One way I can think of to do this would store the variable names as strings in a list, then use the exec function. This function always returns 0. It accepts a string argument and then executes that string as valid python code. So:
# Where allvars contains string names of variables
...
for i, ix in enumerate(allvars):
exec(f"{allvars[i]} = {ix} + 1")
Another way would use the locals() function, which gives you a dictionary with names and values of variables and any other names:
# using locals() function
# Make a list of variable names
allvars_strings = ['a','b','c','d','e']
# Slightly simpler
for i in allvars_strings:
locals()[i] = locals()[i] + 1
string = ''
print('\n\n')
for i in allvars_strings:
string += str(locals()[i]) + ', '
print(string[:-2])
I have code similar to the following in a Python module:
_x = None
def set_x():
global _x
_x = # some value (will always be the same)
def worker():
if _x is None:
set_x()
Can threads created in a client of this module safely call worker()?
X is used in a read-only mode elsewhere in the module; once set it is never changed.
Yes, I realize that x could be encapsulated in a class and the threads could create their own instances of that class on the stack and thus obtain thread-safety, but assume for the purposes of this question that a class-based implementation is not feasible or desirable (e.g., the module represents a Singleton where the cost of setting x is relatively high and we want to set it as infrequently as is practical).
Assuming you are using a global variable in set_x:
x = None
def set_x():
global x
x = "foo"
def worker():
if not x:
set_x()
There is a race condition between if not x and actually setting x. So multiple threads may assign a value to x. That's mostly harmless for immutable objects but problematic for mutable objects like lists where reassignment loses values in the existing x. And by "mostly", even objects whose values compare may have different id's, so == may work but is may fail:
>>> a = "foo"
>>> b = "bar"
>>> c = a + b
>>> d = a + b
>>> c == d
True
>>> c is d
False
And of course code paths that don't play the set_x game may find that x changes from None to your expected value while they execute.
Especially because x is expensive to calculate, a good approach is to hide it behind a getter. Now everyone has to play the same game and only one of the threads will actually do the work.
_x = None
_x_lock = threading.Lock()
def get_x():
global _x
if _x is None:
with _x_lock:
if _x is None:
_x = "foobar"
return _x
I have written a function to calculate the heading between two points only if a vehicle reports that it's moving and that the vehicle has moved 20cm between points.
The function uses static variables - or at least it would if it worked - to keep track of previous positions and heading values.
Here is the code:
def withCan(pos):
eastdist = pos[0]-previous_pos[0]
northdist = pos[1]-previous_pos[1]
canflag = pos[2]
if (canflag == 1 or canflag==2):
if (previous_canflag == 1 and canflag == 2):
previous_heading += 180.0
previous_canflag = canflag
elif (previous_canflag == 2 and canflag == 1):
previous_heading += 180.0
previous_canflag = canflag
else:
previous_canflag = canflag
if ( (canflag == 1 or canflag == 2) and math.sqrt(northdist*northdist+eastdist*eastdist) > canstep ):
previous_heading = math.degrees(math.atan2(eastdist, northdist))
previous_pos[0] = pos[0]
previous_pos[1] = pos[1]
return previous_heading
withCan.previous_pos = [0.0,0.0]
withCan.previous_heading = 0.0
withCan.previous_canflag = 0
withCan.canstep = 0.2
positions = backandforth([100,100]) #populates an array of form [x,y,canflag]
for p in positions:
print withCan(p)
I am getting an error that says eastdist = pos[0]-previous_pos[0]
NameError: global name 'previous_pos' is not defined. Please could someone explain the cause of this error?
When you do this:
def foo():
pass
foo.name = 1
You are not creating a global name name. Instead you are adding a property to the foo function! You can access it with:
def foo():
return foo.name
foo.name = 1
But that is quite weird. If you need a global name, just do it:
def foo():
global name
name += 1
return name
name = 1
Remember that if you want to modify the global name from the function, you have to declare it as global. If you fail to do this, you can use it but you cannot assign to it.
Your confusion with static names may come from using classes. But note that in your code withCan is not a class, it is a plain function!
It looks like what you are trying to do is writing a class...
class WithCan():
def __init(self, previous_pos)__:
self.previous_pos=previous_pos
def withCan(self, pos):
# your function as class method
Then you could initialize an instance
withCan=WithCan(previous_pos)
and access it
withCan.previous_pos=...
You can do static variables in Python using function attributes, but you need to use the full name inside the function to access those attributes.
Here's a short demo.
def test(a):
print a, a + test.b
test.b += 1
test.b = 5
test(3)
test(10)
output
3 8
10 16
However, it would be more usual to do this sort of thing using a class, as shown in Tim's answer.
Another way to do statics in Python is to give your function default mutable arguments, but many people are uncomfortable with that. But if you're curious, please see “Least Astonishment” in Python: The Mutable Default Argument.
Let me contribute a perhaps more streamlined way of emulating static variables in functions that could make the OP's example maybe easier to read:
def with_can(pos):
if not hasattr(with_can, "canflag"):
# set up and initialise the static variables
with_can.canflag = 0
with_can.previous_pos = [0.0,0.0]
with_can.previous_heading = 0.0
with_can.canstep = 0.2
# ... use them ...
eastdist = pos[0]-with_can.previous_pos[0]
# ... etc ...
Basically at the first invocation we detect that one of the "static" variables (canflag) is not yet there so we add and initialise all of them. After that they can be used as indicated.
However, as others have pointed out already, it is much better to write a class with data members instead of these "static" function variables.
Apologies if someone has already addressed this, but I couldn't find an answer.
I have a problem where I overrode __cmp__ for a custom class, similar to this:
class MyClass(object):
def __init__(self, prop):
self.prop = prop
def __cmp__(self, other):
return cmp(self.prop, other.prop)
a = MyClass(1)
b = MyClass(2)
c = MyClass(3)
d = MyClass(1)
my_list = [a, b, c]
print a in my_list
print b in my_list
print c in my_list
print d in my_list
# Output:
#
# True
# True
# True
# True
Reading the docs tells me that this is expected, because:
For the list and tuple types, x in y is true if and only if there exists an index i such that x == y[i] is true.
My question is two-fold:
Why does Python (2.7) use == rather than is to test list membership?
Is there a better way to be doing this, such that I can test whether a specific instance of my class is actually in a container and be able to compare instances based on their attributes?
Why does Python (2.7) use == rather than is to test list membership?
Because equality is usually more interesting than identity for containment checking, especially when two different objects can have the same value.
>>> ('Hello, ' + 'world!') is 'Hello, world!'
False
>>> ('Hello, ' + 'world!') == 'Hello, world!'
True
Is there a better way to be doing this, such that I can test whether a specific instance of my class is actually in a container and be able to compare instances based on their attributes?
Not especially.
The list contains a value equal to the variable, not a reference to the variable. Think about it this way. Are these two code samples the same? What makes them different?
class Person:
def __init__(self, name):
self.name = name
def __eq__(self, other):
return self.name == other.name
# sample one
sam = Person('Sam')
bob = Person('Bob')
people = [sam, bob]
The first sample is when you should use the is keyword. You're trying to test if the object in the list is the object outside of the list, meaning the sam inside the list is stored in the same place in memory as the sam outside the list.
# sample two
sam = Person('Sam')
bob = Person('Bob')
people = [Person('Sam'), Person('Bob')]
This sample is when you should use the double equals keyword. You're trying to test if the object in the list has the value when compared to the object outside of the list. Notice, that because we instantiated to different people with the name Sam, they will be equal in value but not the same object.