Given a function:
def A(a, b, c):
a *= 2
b *= 4
c *= 8
return a+b+c
How can I set the 'c' var to be called-by-reference, so if i call d = A(a,b,c), c will point to the same int object, and be updated from within the function?
You're getting into murky territory: you're talking about declaring global (or nonlocal) variables in functions, which should simply not be done. Functions are meant to do one thing, and do them without the consent of other values or functions affecting their state or output.
It's difficult to suggest a working example: are you alright with having copies of the variables left behind for later reference? You could expand this code to pass back a tuple, and reference the members of the tuple if needed, or the sum:
>>> def A(a, b, c):
return (a*2, b*4, c*8)
>>> d = A(2, 4, 8)
>>> sum(d)
84
>>> d[-1] #or whatever index you'd need...this may serve best as a constant
64
You can do this if c is a list:
c = [2]
def A(a, b, c):
a *= 2
b *= 4
c[0] *= 8
return a+b+c[0]
print c # gives [16]
You can't. Python cannot do that.
What you can do is pass a wrapper that has __imul__() defined and an embedded int, and the augmented assignment will result in the embedded attribute being mutated instead.
All calls in Python are "by reference". Integers are immutable in Python. You can't change them.
class C:
def __init__(self, c):
self.c = c
def __call__(self, a, b):
a *= 2
b *= 4
self.c *= 8
return a + b + self.c
Example
A = C(1)
print A(1, 1), A.c
print A(1, 1), A.c
Output
14 8
70 64
Related
I have some function which uses outside variables. A (substantially) simplified example:
a = 2
b = 3
def f(x):
return x * a + b
While I need a and b in f, I don't need them anywhere else. In particular, one can write a = 5, and that will change the behavior of f. How should I make a and b invisible to the outside?
Other languages allow me to write roughly the following code:
let f =
a = 2
b = 3
lambda x: x * a + b
What I want:
f must work as intended and have the same signature
a and b must be computed only once
a and b must not exist in the scope outside of f
Assignments a = ... and b = ... don't affect f
The cleanest way to do this. E.g. the following solution formally works, but it introduces g and then deletes it, which I don't like (e.g. there is a risk of overriding an existing g and I believe that it's simply ugly):
def g():
a = 2
b = 3
return lambda x: x * a + b
f = g()
del g
One method is to simply use a class. This allows you to place a and b in the scope of the class while f can still access them.
custom class
class F:
def __init__(self):
self.a = 2
self.b = 3
def __call__(self, x):
return x * self.a + self.b
f = F()
f(1)
# returns:
5
If you don't like having to call the class constructor, you can override __new__ to essentially create a callable with internal stored variables. This is an antipattern though and not very pythonic.
custom callable
class f:
a = 2
b = 3
def __new__(cls, x):
return x * cls.a + cls.b
f(1)
# returns:
5
This approach is based on the answers provided in this thread, though scoped to the specific problem above. You can use a decorator to update the global variables available to the function while also storin a and b within a closure.
decorator with closure
from functools import wraps
def dec_ab(fn):
a = 2
b = 3
#wraps(fn)
def wrapper(*args, **kwargs):
# get global scope
global_scope = f.__globals__
# copy current values of variables
var_list = ['a', 'b']
current_vars = {}
for var in var_list:
if var in global_scope:
current_vars[var] = global_scope.get(var)
# update global scope
global_scope.update({'a': a, 'b': b})
try:
out = fn(*args, **kwargs)
finally:
# undo the changes to the global scope
for var in var_list:
global_scope.pop(var)
global_scope.update(current_vars)
return out
return wrapper
#dec_ab
def f(x):
"""hello world"""
return x * a + b
This preserves the functions signature and keeps a and b from being altered
f(1)
# returns:
5
a
# raises:
NameError: name 'a' is not defined
You can use default arguments to accomplish this. Default arguments are only computed once, when the closure is created (that is why if you have mutable objects as default arguments, the state is retained between invocations).
def f(x, a=2, b=3):
return x * a + b
For example
b = 4
c = []
c.append(b)
print(c)
b += 2
print(c)
I was hoping I would get
4
6
but I got
4
4
Any chance I could add the element as a pointer. And then if I reference that element with c[0] I'd be referencing the b?
here c and b are different variables where c is list and b is int so if you add some on b it doesn't mean and c will be updated but if you add some number on b and append it again on c
there will be a change
b = 4
c = []
c.append(b)
print(c)
b += 2
c.append(c)
print(c)
and what you will get is
[4, 6]
and I think is clear that a c didn't change in your question
You can do that with a class:
class Number:
def __init__(self, number):
self.number = int(number)
def __str__(self):
return str(self.number)
def __repr__(self):
return self.__str__()
def __add__(self, other):
self.number = self.number + other
b = Number(4)
c = []
c.append(b)
print(c)
b += 2
print(c)
You will get:
4
6
Refer this below link to understand how the reference works in python:
How do I pass a variable by reference?
So, in short,
python objects i.e. booleans, integers, floats, strings, and tuples are immutable, which means that after you create the object and assign some value to it, you can't modify that value.
Hence, when you do b += 2, here, b will point to a new memory reference.
Proof:
>>> b=10
>>> id(b)
1734146112
>>> b+=2
>>> id(b)
1734146144
>>>
However, as #yoav mentioned above,
you can also do this by tweaking the default behaviour of Number class but beware; you should use it very carefully as you might not need this behaviour for every class.
I would like to define a Python function in which a parameter is external if it is declared when the function is called and it is internal if it is not mentioned into the function calling.
An easy example will clarify my issue:
def func(a, b):
c = 4
try:
b
except:
b= c
return a + b
Now I want something in which I can write
func(2, 1)
and provide me the result (2 + 1 = 3), and I can even write
func(2)
and the function will generate a solution that is c + a = 6.
How can I do that?
You are describing a default argument. Python does that for you (see the docs):
def func(a, b=4):
return a + b
Alternatively, if more logic is involved, you might want to consider:
def func(a, b=None):
if b is None:
try:
b = fancy_function()
except FancyException:
b = 4
return a + b
The same holds for mutable default values:
def func(a, b=None):
if b is None:
b = []
b.append(a)
return b
Simply write
def func(a, b=4):
return a + b
>>> func(2)
6
>>> func(2, 1)
3
If the default value of b is not known beforehand, you can do the following:
def func(a, b=None):
if b is None:
b = some_function_that_generates_b()
return a + b
Consider the following function, which we'd like to not be constant on integers a, but which always returns (1,2):
def foo(a):
b = 1
c = 2
def bar(b,c):
b = b + a
c = c + a
bar(b,c)
return (b,c)
If I understand correctly, the idiomatic way to implement bar(b,c) would be to give it no parameters at all and declare b and c nonlocal inside its definition. I'm curious, however: how do we make an inner function have nonlocal effects on its parameters?
As stated in this answer for Python 2.x:
Python doesn't allow you to reassign the value of a variable from an
outer scope in an inner scope (unless you're using the keyword
"global", which doesn't apply in this case).
This will return (2,3):
def foo(a):
b = 1
c = 2
def bar(b,c):
b = b + a
c = c + a
return b, c
b, c = bar(b,c)
return (b,c)
print(foo(1))
Make b and c function attributes.
def foo(a):
foo.b = 1
foo.c = 2
def bar():
foo.b = foo.b + a
foo.c = foo.c + a
bar()
return (foo.b,foo.c)
Notice you no longer pass b or c into the function bar.
Function parameters are always locals. You could pass in mutable objects however, and apply your operations to the indirectly referenced value:
def foo(a):
b = [1]
c = [2]
def bar(b, c):
b[0] += a
c[0] += a
bar(b, c)
return (b[0], c[0])
The changes you make to the mutable object are shared with any other references to those objects, including locals in foo().
However, if you want something to be a closure, just make it a closure. There are no use cases for this that cannot be handled by nonlocal values and return.
This question already has answers here:
How to get/set local variables of a function (from outside) in Python? [duplicate]
(5 answers)
Closed 9 years ago.
Let us consider the following program
def fib(n):
result = []
a, b = 0, 1
while b < n:
result.append(b)
a, b = b, a+b
c = result
print c
return result
f100 = fib(100)
print result
#print c
How can I access the variable 'c' from out side the function? Is it possible? I know
print result
will give the same, but i want to know is there any method to access 'c' outside the function?
You could declare c as global, although that's not generally a pattern you'd want to encourage. You'd do that like this:
c = None
def fib(n):
global c
result = []
a, b = 0, 1
while b < n:
result.append(b)
a, b = b, a+b
c = result
return result
f100 = fib(100)
print result
print c
You could also restructure your function as a class with a __call__ method which would let you expose internal values as attributes, such as:
class fibber(object):
def __init__(self):
self.c = None
def __call__(self, n):
result = []
a, b = 0, 1
while b < n:
result.append(b)
a, b = b, a+b
self.c = result
return result
fib = fibber()
f100 = fib(100)
print result
print fib.c
Local variables only exist in the context of the function they are defined in. That's what makes them local. So the whole variable c does not exist anymore once the function terminates and returns its result value.
You can of course save the value of that variable in a different one, e. g. a field of the function itself:
fib.c = c
Since the function itself will exist also after it terminated, so will its fields, and so will fib.c.
But I must stress that this is just a hack. Normally if you want to access a value outside of a function it is a good idea to make the variable holding that value not local.
You can declare c a global variable:
def fib(n):
global c
result = []
a, b = 0, 1
while b < n:
result.append(b)
a, b = b, a+b
c = result
print c
return result
result = fib(10)
print result
print c