modify a main variable from a function that is inside another function - python-3.x

I want to modify variables "player_row" and "player_column" from the function "second_function" which is inside the function "first_function".
If I modify those variables from first_function it works, but my program requires the function "second_function" modifies those variables.
Code:
#functions
def first_function(a, b):
second_function(a, b)
print(f"Inside first_function: {a}, {b}")
def second_function(x, y):
x = 5
y = 10
print(f"Inside second_function: {x}, {y}")
#Main
#Variables
player_row = 0
player_column = 0
first_function(player_row, player_column)
print(f"Inside MAIN: {player_row}, {player_column}")
Result:
Inside second_function: 5, 10
Inside first_function: 0, 0
Inside MAIN: 0, 0

This is because Python is a "pass-by-value" language. Instead of passing a reference that you could change to refer to another value, you pass the value itself.
You have two options: one is to return a value out of your functions that represents the new state of your program, the other is to pass in a value that can be modified. (The difference this works and your original code does not is because the value will still be the same object, in that it will occupy the same memory, but you will have changed the contents of that memory)
Returning a value:
def func_1(row, col):
return func_2(row, col)
def func_2(row, col):
return 5, 10
row, col = func_1(0, 0)
Modifying a mutable object:
from dataclasses import dataclass
#dataclass
class Player:
row: int = 0
col: int = 0
def func_1(player):
func_2(player)
def func_2(player):
player.row = 5
player.row = 10
player = Player()
func_1(player) # Player(row=5, col=10)

You can use this to change the value of variable from another function:
#functions
def first_function(a, b):
a,b = second_function(a, b)
print(f"Inside first_function: {a}, {b}")
def second_function(x, y):
x = 5
y = 10
print(f"Inside second_function: {x}, {y}")
return (x,y)
#Main
#Variables
player_row = 0
player_column = 0
first_function(player_row, player_column)
print(f"Inside MAIN: {player_row}, {player_column}")
You can use a similar return statement in first function as well to changethe value of variables in MAIN

Related

Create new instance of a given object, using same arguments provided in __init__

Given any iterator, I want to be able to pass this iterator into my function, to return a new iter object that cycles through the given iter infinitely. E.G
class range:
def __init__(self, min, max, step):
self.min = min
self.max = max
self.step = counter
[...] other vars
def __next__(self):
if [.............]
You get the jist. I have a bunch of custom iterators like this, all created with diff args/params. I know I can use vars to get the variables... but how do you distinguish between vars passed into init to create this object, and others vars? Vars returnsmin/max/counter + all the other vars
I have a class like this:
class Cycle:
def __init__(self, iterable):
self.iterable = iterable
def __next__(self):
next_val = next(self.iterable, None)
if next_val is None:
#RESET iterable or create new instance of iterable with same _init_ args used to create it
next_val = next(self.iterable)
return next_val
Intended use:
r = range(0,4,2)
next(r) = 0
next(r) = 2
next(r) = 4
next(r) = stopiterationerror
c = Cycle(r)
next(c) = 0
next(c) = 2
next(c) = 4
next(c) = 0
next(c) = 2
next(c) = 4
.......
I want to keep this cycle function clean and simple if I can. Ideally I would want to simply self.iterable.reset, but it seems like resetting an iterable is not possible in python and I need to create a brand new instance. I know I could pass in *args into cycle, but is it possible to avoid that?
edit currently working:
class Cycle:
def __init__(self, iterable):
self.iterable = iterable
self.copy = copy(iterable)
def __next__(self):
next_val = next(self.iterable, None)
if next_val is None:
self.iterable = self.copy
self.copy = copy(self.copy)
next_val = next(self.iterable, None)
return next_val

How to use ray parallelism within a class in python?

I want to use the ray task method rather than the ray actor method to parallelise a method within a class. The reason being the latter seems to need to change how a class is instantiated (as shown here). A toy code example is below, as well as the error
import numpy as np
import ray
class MyClass(object):
def __init__(self):
ray.init(num_cpus=4)
#ray.remote
def func(self, x, y):
return x * y
def my_func(self):
a = [1, 2, 3]
b = np.random.normal(0, 1, 10000)
result = []
# we wish to parallelise over the array `a`
for sub_array in np.array_split(a, 3):
result.append(self.func.remote(sub_array, b))
return result
mc = MyClass()
mc.my_func()
>>> TypeError: missing a required argument: 'y'
The error arises because ray does not seem to be "aware" of the class, and so it expects an argument self.
The code works fine if we do not use classes:
#ray.remote
def func(x, y):
return x * y
def my_func():
a = [1, 2, 3, 4]
b = np.random.normal(0, 1, 10000)
result = []
# we wish to parallelise over the list `a`
# split `a` and send each chunk to a different processor
for sub_array in np.array_split(a, 4):
result.append(func.remote(sub_array, b))
return result
res = my_func()
ray.get(res)
>>> [array([-0.41929678, -0.83227786, -2.69814232, ..., -0.67379119,
-0.79057845, -0.06862196]),
array([-0.83859356, -1.66455572, -5.39628463, ..., -1.34758239,
-1.5811569 , -0.13724391]),
array([-1.25789034, -2.49683358, -8.09442695, ..., -2.02137358,
-2.37173535, -0.20586587]),
array([ -1.67718712, -3.32911144, -10.79256927, ..., -2.69516478,
-3.1623138 , -0.27448782])]```
We see the output is a list of 4 arrays, as expected. How can I get MyClass to work with parallelism using ray?
a few tips:
It's generally recommended that you only use the ray.remote decorator on functions or classes in python (not bound methods).
You should be very very careful about calling ray.init inside the constructor of a function, since ray.init is not idempotent (which means your program will fail if you instantiate multiple instances of MyClass). Instead, you should make sure ray.init is only run once in your program.
I think there's 2 ways of achieving the results you're going for with Ray here.
You could move func out of the class, so it becomes a function instead of a bound method. Note that in this approach MyClass will be serialized, which means that changes that func makes to MyClass will not be reflected anywhere outside the function. In your simplified example, this doesn't appear to be a problem. This approach makes it easiest to achieve the most parallelism.
#ray.remote
def func(obj, x, y):
return x * y
class MyClass(object):
def my_func(self):
...
# we wish to parallelise over the array `a`
for sub_array in np.array_split(a, 3):
result.append(func.remote(self, sub_array, b))
return result
The other approach you could consider is to use async actors. In this approach, the ray actor will handle concurrency via asyncio, but this comes with the limitations of asyncio.
#ray.remote(num_cpus=4)
class MyClass(object):
async def func(self, x, y):
return x * y
def my_func(self):
a = [1, 2, 3]
b = np.random.normal(0, 1, 10000)
result = []
# we wish to parallelise over the array `a`
for sub_array in np.array_split(a, 3):
result.append(self.func.remote(sub_array, b))
return result
Please see this code:
#ray.remote
class Prime:
# Constructor
def __init__(self,number) :
self.num = number
def SumPrime(self,num) :
tot = 0
for i in range(3,num):
c = 0
for j in range(2, int(i**0.5)+1):
if i%j == 0:
c = c + 1
if c == 0:
tot = tot + i
return tot
num = 1000000
start = time.time()
# make an object of Check class
prime = Prime.remote(num)
print("duration =", time.time() - start, "\nsum_prime = ", ray.get(prime.SumPrime.remote(num)))

How do I return a value from a higher-order function?

guys how can I make it so that calling make_repeater(square, 0)(5) return 5 instead of 25? I'm guessing I would need to change the line "function_successor = h" because then I'm just getting square(5) but not sure what I need to change it to...
square = lambda x: x * x
def compose1(h, g):
"""Return a function f, such that f(x) = h(g(x))."""
def f(x):
return h(g(x))
return f
def make_repeater(h, n):
iterations = 1
function_successor = h
while iterations < n:
function_successor = compose1(h, function_successor)
iterations += 1
return function_successor
it needs to satisfy a bunch of other requirements like:
make_repeater(square, 2)(5) = square(square(5)) = 625
make_repeater(square, 4)(5) = square(square(square(square(5)))) = 152587890625
To achieve that, you have to use the identity function (f(x) = x) as the initial value for function_successor:
def compose1(h, g):
"""Return a function f, such that f(x) = h(g(x))."""
def f(x):
return h(g(x))
return f
IDENTITY_FUNCTION = lambda x: x
def make_repeater(function, n):
function_successor = IDENTITY_FUNCTION
# simplified loop
for i in range(n):
function_successor = compose1(function, function_successor)
return function_successor
if __name__ == "__main__":
square = lambda x: x * x
print(make_repeater(square, 0)(5))
print(make_repeater(square, 2)(5))
print(make_repeater(square, 4)(5))
and the output is
5
625
152587890625
This isn't most optimal for performance though since the identity function (which doesn't do anything useful) is always part of the composed function, so an optimized version would look like this:
def make_repeater(function, n):
if n <= 0:
return IDENTITY_FUNCTION
function_successor = function
for i in range(n - 1):
function_successor = compose1(function, function_successor)
return function_successor

How to define a callable derivative function

For a project I am working on, I am creating a class of polynomials that I can operate on. The polynomial class can do addition, subtraction, multiplication, synthetic division, and more. It also represents it properly.
For the project, we are required to do create a class for Newton's Method. I was able to create a callable function class for f, such that
>f=polynomial(2,3,4)
>f
2+3x+4x^2
>f(3)
47
I have a derivative function polynomial.derivative(f) outputs 3+8x.
I want to define a function labeled Df so that in my Newtons Method code, I can say, Df(x). It would work so that if x=2:
>Df(2)
19
The derivative of a polynomial is still a polynomial. Thus, instead of returning the string 3+8x, your polynomial.derivative function should return a new polynomial.
class polynomial:
def __init__(c, b, a):
self.coefs = [c, b, a]
[...]
def derivative(self):
return polynomial(*[i*c for i,c in enumerate(self.coefs) if i > 0], 0)
Hence you can use it as follow:
> f = polynomial(2, 3, 4)
> Df = f.derivative()
> f
2+3x+4x^2
> Df
3+8x+0x^2
> f(3)
47
> Df(2)
19
Edit
Of course, it is enumerate and not enumerates. As well, the __init__ misses the self argument. I code this directly on SO without any syntax check.
Of course you can write this in a .py file. Here is a complete working example:
class Polynomial:
def __init__(self, c, b, a):
self.coefs = [c, b, a]
self._derivative = None
#property
def derivative(self):
if self._derivative is None:
self._derivative = Polynomial(*[i*c for i,c in enumerate(self.coefs) if i > 0], 0)
return self._derivative
def __str__(self):
return "+".join([
str(c) + ("x" if i > 0 else "") + (f"^{i}" if i > 1 else "")
for i, c in enumerate(self.coefs)
if c != 0
])
def __call__(self, x):
return sum([c * (x**i) for i, c in enumerate(self.coefs)])
if __name__ == '__main__':
f = Polynomial(2, 3, 4)
print(f"f: y={f}")
print(f"f(3) = {f(3)}")
print(f"f': y={f.derivative}")
print(f"f'(2) = {f.derivative(2)}")
f: y=2+3x+4x^2
f(3) = 47
f': y=3+8x
f'(2) = 19
You can rename the property with the name you prefer: derivative, Df, prime, etc.

Trying to use a function that returns three values as the input for a function that takes 3 values

I have a function that does a few things, but ultimately returns three lists.
I have another function, a set method in a class, which takes three inputs.
When I try to use the first function as the argument for the set function, it complains that there's not enough inputs, despite the fact its returning the right amount. Is there a way around this? Should I just declare some temporary local variables to do this?
a simplified version of my code
class hello(object):
a, b, c = 0, 0, 0
def __init__(self, name):
self.name = name
def setThings(one, two, three):
self.a = one
self.b = two
self.c = three
def someStuff(x, y, z):
newX = x * 1337
newY = y * 420
newZ = z * 69
return newX, newY, newZ
first = int(input("first"))
second = int(input("second"))
third = int(input("third"))
kenzi = hello(input("name pls"))
kenzi.setThings(someStuff(first, second, third))
Add a asterix before the function when calling it as a argument.
kenzi.setThings(*someStuff(first, second, third))

Resources