Decrementing Function Arguments (PYTHON) - python

I'm calling functions similar to those that follow, inside a loop:
def bigAnim(tick,firstRun):
smallAnim(x,y,duration)
#more anims and logic...
def smallAnim(x, y,duration):
duration -= 1
if duration != 0:
Anim.blit(screen,(x ,y))
Anim.play()
else:
Anim.stop()
loopedOnce = True
return loopedOnce
Now say I were to call the smallAnim inside the big anim as follows:
def bigAnim(tick,firstRun):
smallAnim(0,50,5)
smallAnim is now being called indefinitely, as duration will never go lower than 4 (being reset to 5 every time it's called in the loop). What would be the best way to solve this problem?

You need to do the counting in bigAnim and only call smallAnim() when the value is greater than zero.
Or you can return the current duration:
def bigAnim(tick,firstRun):
duration = smallAnim(x,y,duration)
#more anims and logic...
def smallAnim(x, y, duration):
duration -= 1
if duration > 0:
Anim.blit(screen,(x ,y))
Anim.play()
return duration
Your underlying problem is Python does pass the references to the variables, but integers are immutable.
This is a little easier to understand with strings:
The function
def foo(s):
s = " world"
will only modify s local to the function if you call foo("hello"). The typical pattern you'll see instead is:
def foo(s):
return s + " world"
And then ... print foo("hello")

Related

Detect the last inline method call of a class

Let's say we have a python class with methods intended to be called one or more times inline. My goal is to make methods behave differently when they are invoked last in an inline chain of calls.
Example:
class InlineClass:
def __init__(self):
self.called = False
def execute(self):
if self.called:
print('do something with awareness of prior call')
return self
else:
print('first call detected')
self.called = True
return self
def end(self):
print ('last call detected, do something with awareness this is the last call')
self.called = False
return self
x = InlineClass()
x.execute().execute().execute().end() # runs 3 execute calls inline and end.
The example above only knows it has reached the last inline call once the end method is invoked. What I would like to do, in essence, is to make that step redundant
QUESTION
Keeping in mind the intent for this class's methods to always be called one or more times inline, is there an elegant way to format the class so it is aware it has reached its last inline method call, and not necessitate the end call as in the example above.
Instead of chaining the functions, you can try creating a function that handles passing different parameters depending on how many times the function has been / will be called.
Here is some example code:
class Something:
def repeat(self, function, count):
for i in range(count):
if i == 0:
function("This is the first time")
elif i == count - 1:
function("This is the last time")
else:
function("This is somewhere in between")
def foo_function(self, text):
print(text)
foo = Something()
foo.repeat(foo.foo_function, 5)
foo.repeat(foo.foo_function, 2)
foo.repeat(foo.foo_function, 6)
foo.repeat(foo.foo_function, 8)
Output:
This is the first time
This is somewhere in between
This is somewhere in between
This is somewhere in between
This is the last time
This is the first time
This is the last time
This is the first time
This is somewhere in between
This is somewhere in between
This is somewhere in between
This is somewhere in between
This is the last time
This is the first time
This is somewhere in between
This is somewhere in between
This is somewhere in between
This is somewhere in between
This is somewhere in between
This is somewhere in between
This is the last time
You need to return modified copies instead of self, here is the example with behaviour you described:
class InlineClass:
def __init__(self, counter=0):
self.counter = counter
def execute(self):
return InlineClass(self.counter+1)
def __str__(self):
return f'InlineClass<counter={self.counter}>'
x = InlineClass()
print(x)
# => InlineClass<counter=0>
y = x.execute().execute().execute()
print(y)
# => InlineClass<counter=3>
print(x.execute().execute().execute())
# => InlineClass<counter=3>
print(y.execute().execute().execute())
# => InlineClass<counter=6>

Mocking API call embedded in some objects and changing behavior based on inputs within object

This is a continuation of the SO question asked here but with a more complicated pattern than originally requested. My main intention is to try to mock an API call based on values passed to its caller. The API call has no idea of the values passed to its caller but needs to provide the correct behavior so that the caller can be tested fully. I am using time to determine which behavior I want when I want it.
Given a an object:
# some_object.py
from some_import import someApiCall
class SomeObject():
def someFunction(time, a, b, c):
apiReturnA = someApiCall(a)
returnB = b + 1
apiReturnC = someApiCall(c)
return [apiReturnA, returnB, apiReturnC]
that is created by another object with entry point code:
# some_main.py
import some_object
class myMainObject():
def entry_point(self, time):
someObj = some_object.SomeObject()
if 'yesterday' == time:
(a, b, c) = (1, 1, 1)
elif 'today' == time:
(a, b, c) = (2, 2, 2)
elif 'later' == time:
(a, b, c) = (3, 3, 3)
elif 'tomorrow' == time:
(a, b, c) = (4, 4, 4)
else:
return "ERROR"
return someObj.someFunction(time, a, b, c)
how can I get someApiCall to change based on the time argument?
# some_import.py
def someApiCall(var):
print("I'm not mocked! I'm slow, random and hard to test")
return var + 2
Here is an example test case
# test_example.py
import some_main
def amend_someApiCall_yesterday(var):
# Reimplement api.someApiCall
return var * 2
def amend_someApiCall_today(var):
# Reimplement api.someApiCall
return var * 3
def amend_someApiCall_later(var):
# Just wrap around api.someApiCall. Call the original function afterwards. Here we can also put
# some conditionals e.g. only call the original someApiCall if the var is an even number.
import some_import
var *= 4
return some_import.someApiCall(var)
def someObject_decorator_patch(someFunction, mocker, *args):
def wrapper(time, a, b, c):
# If x imports y.z and we want to patch the calls to z, then we have to patch x.z. Patching
# y.z would still retain the original value of x.z thus still calling the original
# functionality. Thus here, we would be patching src.someApiCall and not api.someApiCall.
if time == "yesterday":
mocker.patch("some_object.someApiCall", side_effect=amend_someApiCall_yesterday)
elif time == "today":
mocker.patch("some_object.someApiCall", side_effect=amend_someApiCall_today)
elif time == "later":
mocker.patch("some_object.someApiCall", side_effect=amend_someApiCall_later)
elif time == "tomorrow":
mocker.patch("src.someApiCall", return_value=0)
else:
# Use the original api.someApiCall
pass
return someFunction(time, a, b, c)
return wrapper
def test_some_main(mocker):
results = 0
uut = some_main.myMainObject()
times = ['yesterday', 'today', 'later', 'tomorrow']
mocker.patch.object(some_main.some_object.SomeObject, 'someFunction', someObject_decorator_patch)
for time in times:
results = uut.entry_point(time)
print(results)
assert 0 != results
The test case doesn't get the result I want (it returns a function pointer).
Here is an improvised solution of https://stackoverflow.com/a/67498948/11043825 without using decorators.
As already pointed out, we still need to intercept the calls to the function that accepts the time argument which indicates how someApiCall would behave, which is either entry_point or someFunction. Here we would intercept someFunction.
Instead of implementing python decorator on someFunction which then needs to call that explicitly created decorated function, here we would amend (well this still follows the decorator design pattern) the someFunction in-place and make it available to the rest of the source code calls without explicitly changing the call to the decorated function. This is like an in-place replacement of the original functionalities, where we would replace (or rather wrap around) the original functionality with an updated one which would perform an assessment of the time before calling the original functionality.
Also for your reference, I solved it for 2 types of functions, a class method src.SomeClass.someFunction and a global function src.someFunction2.
./_main.py
import src
class MyMainClass:
def __init__(self):
self.var = 0
def entry_point(self, time):
someObj = src.SomeClass()
self.var += 1
if self.var >= 10:
self.var = 0
ret = f'\n[1]entry_point({time})-->{someObj.someFunction(time, self.var)}'
self.var += 1
ret += f'\n[2]entry_point({time})-->{src.someFunction2(time, self.var)}'
return ret
./src.py
class SomeClass:
def someFunction(self, time, var):
return f'someFunction({time},{var})-->{someSloowApiCall(var)}'
def someFunction2(time, var):
return f'someFunction2({time},{var})-->{someSloowApiCall2(var)}'
./api.py
def someSloowApiCall(var):
return f'someSloowApiCall({var})-->{special_message(var)}'
def someSloowApiCall2(var):
return f'someSloowApiCall2({var})-->{special_message(var)}'
def special_message(var):
special_message = "I'm not mocked! I'm slow, random and hard to test"
if var > 10:
special_message = "I'm mocked! I'm not slow, random or hard to test"
return special_message
./test_main.py
import _main, pytest, api
def amend_someApiCall_yesterday(var):
# Reimplement api.someSloowApiCall
return f'amend_someApiCall_yesterday({var})'
def amend_someApiCall_today(var):
# Reimplement api.someSloowApiCall
return f'amend_someApiCall_today({var})'
def amend_someApiCall_later(var):
# Just wrap around api.someSloowApiCall. Call the original function afterwards. Here we can also put
# some conditionals e.g. only call the original someSloowApiCall if the var is an even number.
return f'amend_someApiCall_later({var})-->{api.someSloowApiCall(var+10)}'
def amend_someApiCall_later2(var):
# Just wrap around api.someSloowApiCall2. Call the original function afterwards. Here we can also put
# some conditionals e.g. only call the original someSloowApiCall2 if the var is an even number.
return f'amend_someApiCall_later2({var})-->{api.someSloowApiCall2(var+10)}'
def get_amended_someFunction(mocker, original_func):
def amend_someFunction(self, time, var):
if time == "yesterday":
mocker.patch("_main.src.someSloowApiCall", amend_someApiCall_yesterday)
# or
# src.someSloowApiCall = amend_someApiCall_yesterday
elif time == "today":
mocker.patch("_main.src.someSloowApiCall", amend_someApiCall_today)
# or
# src.someSloowApiCall = amend_someApiCall_today
elif time == "later":
mocker.patch("_main.src.someSloowApiCall", amend_someApiCall_later)
# or
# src.someSloowApiCall = amend_someApiCall_later
elif time == "tomorrow":
mocker.patch("_main.src.someSloowApiCall", lambda var: f'lambda({var})')
# or
# src.someSloowApiCall = lambda var: 0
else:
pass
# or
# src.someSloowApiCall = someSloowApiCall
return original_func(self, time, var)
return amend_someFunction
def get_amended_someFunction2(mocker, original_func):
def amend_someFunction2(time, var):
if time == "yesterday":
mocker.patch("_main.src.someSloowApiCall2", amend_someApiCall_yesterday)
# or
# src.someSloowApiCall2 = amend_someApiCall_yesterday
elif time == "today":
mocker.patch("_main.src.someSloowApiCall2", amend_someApiCall_today)
# or
# src.someSloowApiCall2 = amend_someApiCall_today
elif time == "later":
mocker.patch("_main.src.someSloowApiCall2", amend_someApiCall_later2)
# or
# src.someSloowApiCall2 = amend_someApiCall_later
elif time == "tomorrow":
mocker.patch("_main.src.someSloowApiCall2", lambda var : f'lambda2({var})')
# or
# src.someSloowApiCall2 = lambda var: 0
else:
pass
# or
# src.someSloowApiCall2 = someSloowApiCall2
return original_func(time, var)
return amend_someFunction2
#pytest.mark.parametrize(
'time',
[
'yesterday',
'today',
'later',
'tomorrow',
'whenever',
],
)
def test_entrypointFunction(time, mocker):
mocker.patch.object(
_main.src.SomeClass,
"someFunction",
side_effect=get_amended_someFunction(mocker, _main.src.SomeClass.someFunction),
autospec=True, # Needed for the self argument
)
# or
# src.SomeClass.someFunction = get_amended_someFunction(mocker, src.SomeClass.someFunction)
mocker.patch(
"_main.src.someFunction2",
side_effect=get_amended_someFunction2(mocker, _main.src.someFunction2),
)
# or
# src.someFunction2 = get_amended_someFunction2(mocker, src.someFunction2)
uut = _main.MyMainClass()
print(f'\nuut.entry_point({time})-->{uut.entry_point(time)}')
Output:
$ pytest -rP
=================================== PASSES ====================================
_____________________ test_entrypointFunction[yesterday] ______________________
---------------------------- Captured stdout call -----------------------------
uut.entry_point(yesterday)-->
[1]entry_point(yesterday)-->someFunction(yesterday,1)-->amend_someApiCall_yesterday(1)
[2]entry_point(yesterday)-->someFunction2(yesterday,2)-->amend_someApiCall_yesterday(2)
_______________________ test_entrypointFunction[today] ________________________
---------------------------- Captured stdout call -----------------------------
uut.entry_point(today)-->
[1]entry_point(today)-->someFunction(today,1)-->amend_someApiCall_today(1)
[2]entry_point(today)-->someFunction2(today,2)-->amend_someApiCall_today(2)
_______________________ test_entrypointFunction[later] ________________________
---------------------------- Captured stdout call -----------------------------
uut.entry_point(later)-->
[1]entry_point(later)-->someFunction(later,1)-->amend_someApiCall_later(1)-->someSloowApiCall(11)-->I'm mocked! I'm not slow, random or hard to test
[2]entry_point(later)-->someFunction2(later,2)-->amend_someApiCall_later2(2)-->someSloowApiCall2(12)-->I'm mocked! I'm not slow, random or hard to test
______________________ test_entrypointFunction[tomorrow] ______________________
---------------------------- Captured stdout call -----------------------------
uut.entry_point(tomorrow)-->
[1]entry_point(tomorrow)-->someFunction(tomorrow,1)-->lambda(1)
[2]entry_point(tomorrow)-->someFunction2(tomorrow,2)-->lambda2(2)
______________________ test_entrypointFunction[whenever] ______________________
---------------------------- Captured stdout call -----------------------------
uut.entry_point(whenever)-->
[1]entry_point(whenever)-->someFunction(whenever,1)-->someSloowApiCall(1)-->I'm not mocked! I'm slow, random and hard to test
[2]entry_point(whenever)-->someFunction2(whenever,2)-->someSloowApiCall2(2)-->I'm not mocked! I'm slow, random and hard to test
============================== 5 passed in 0.07s ==============================

While evaluating length of a list that is defined as a class

For example, I got a class named stack,
class stack: #Will be used for probable files!
def __init__(self):
self.data = []
def add(self, element):
self.data.append(element)
def number_of_elements(self):
return len(self.data)
def stackType(self):
if self.number_of_elements == 0:
return 0
elif self.number_of_elements == 1:
return 1
else:
return -1
I then do this:
foo = stack()
print foo.stackType()
I get -1 however I was expecting a return of 1
Why is it so and how can I handle with it?
That's because you did not call the call the method self.number_of_elements; you merely tested to see if it equalled 0 or 1.
Modify your code to actually call the method using this syntax: self.number_of_elements() [notice the use of () to call the method]:
def stackType(self) :
if self.number_of_elements() == 0 :
return 0
elif self.number_of_elements() == 1 :
return 1
else :
return -1
You could also have written it like this:
def stack_type(self):
n = self.number_of_elements()
return -1 if n > 1 else n
which would be an improvement because number_of_elements() will be called once only. In your code the method could be called twice. I renamed the function to be consistent with the Python method naming conventions set out in PEP8.
Because self.number_of_elements is not the same as self.number_of_elements()!
The former is a reference to the function, the latter is a call to the function actually calculating the length of your stack.
self.number_of_elements is a function, so its value is neither zero nor 1

Accept a single string instead of normal parameters

Hello everyone I have a class that works with time, for example you enter two times and you can sum them (for now it doesn't matter that it goes beyond 23:59:59) or multiply them etc. The default input are zeros. There is also a function that returns current time. I can call time = Time() which returns 0:00:00, time = Time(12) returns 12:00:00.
The problem that I have is, I want to call time = Time('now') and it should store there current time using function now(), edits should be done in __init__() (if you don't like now() you can change it). But if i put time_now = '' as first parameter it doesn't work, same when i put it as a last, because when I write time = Time('now') it takes string 'now' and wants to put it to hours.
I know there are time modules which can do this, but this is for learning purposes.
My code:
import random, time
class Time:
def __init__(self, hours=0, minutes=0, seconds=0, time_now=''):
# if time_now == 'now':
# now()
time = abs(3600*hours + 60*minutes + seconds)
self.hour = time//3600
self.min = time//60%60
self.sec = time%60
def __repr__(self):
return '{}:{:02}:{:02}'.format(self.hour,self.min,self.sec)
def __add__(self, other):
if isinstance(other, Time):
return Time(self.hour+other.hour, self.min+other.min, self.sec+other.sec)
if isinstance(other, int):
return Time(self.hour, self.min, self.sec+other)
__radd__ = __add__
def __sub__(self, other):
return Time(seconds = self.__int__() - other.__int__())
def __mul__(self, other):
return Time(self.hour*other.hour, self.min*other.min, self.sec*other.sec)
def __int__(self):
return 3600*self.hour + 60*self.min + self.sec
def __eq__(self, other):
return self.__int__() == other.__int__()
def __lt__(self, other):
return self.__int__() < other.__int__()
#writing out hour/min/sec what we want
def __getitem__(self, key):
if key == 0:
return self.hour
if key == 1:
return self.min
if key == 2:
return self.sec
#time now
def now():
a=(time.localtime()[3:6])
return Time(a[0],a[1],a[2])
Python doesn't have method overloading, so your only option is to "play" with the arguments:
You can do something that IMHO is very bad (downgraded very bad to meeeeh... so, so after reading #ivan-pozdeev's comments in this answer)
class Time:
def __init__(self, hours=0, minutes=0, seconds=0, time_now=''):
if hours == 'now':
tmp_t = now()
self.hour = tmp_t.hour
self.min = tmp_t.min
self.sec = tmp_t.sec
else:
t = abs(3600*hours + 60*minutes + seconds)
self.hour = t//3600
self.min = t//60%60
self.sec = t%60
That... well, that works:
>>> a = Time('now')
>>> print vars(a)
{'sec': 20, 'hour': 15, 'min': 18}
>>>
>>> a = Time(hours=19, minutes=4, seconds=5)
>>> print vars(a)
{'sec': 5, 'hour': 19, 'min': 4}
But that leaves the code in a very weird state. Is very difficult to read. I certainly would try to come with a different approach altogether...
I also changed the time variable within the __init__ to t because that conflicted with the time name from import time
You can use a call with named parameters: Time(time_now='now'), but that looks too reiterative.
If I was using named parameters, I'd instead change it to a boolean and use something like Time(now=True), which looks a bit cleaner.
But I think a better alternative is to make your "now" objects be constructed from your now() function, which I'd move to a static method:
class Time(object):
#...
#classmethod
def now(cls):
a=(time.localtime()[3:6])
return cls(a[0],a[1],a[2])
So you'd construct your now objects like x = Time.now() instead of x = Time('now')
For flexible argument handling beyond what's available with static arguments, you may use *args and/or **kwargs:
def __init__(self, *args, **kwargs):
a_seq=('hours','minutes','seconds')
if len(args)>0 and args[0]=='now':
now_=now()
self.__dict__.update(a,getattr(now_,a) for a in a_seq)
else:
t=0
for i,a in enumerate(a_seq):
try: v=args[i]
except IndexError: v=kwargs.get(a,0)
else: if a in kwargs: raise TypeError("multiple values given for argument `%s'"%a)
t+=v
t*=60
for a in a_seq[-1:0:-1]:
setattr(self,a,t%60)
t//=60
setattr(self,a_seq[0],t)
(I renamed the fields to the same names as arguments to make it easier to use reflection. Reflection is used here to eliminate code duplication (without it, it'd be about 1.5 times longer).)
In this particular case, though, BorrajaX's solution is much simpler because the deviation from a normal static argument list here is only minor (in fact, the try-except-else in the 2nd half of my code do nothing but emulate it).
Almost never try to implement your own time module. It's time consuming and defective. Use time or datetime. All your needs are already implemented somehow.

Python patch existing class

I'm an experienced PHP/Ruby developer but right now I'm fighting Python and I really need your help.
I need to patch existing class by adding static attribute and overwriting static function to use it.
Let me show you example:
class Test():
#staticmethod
def generate():
return 10
But in my test suite I need to get the following class:
class Test():
count = 1
#staticmethod
def generate():
if Test.count < 3:
Test.count += 1
return 1
else:
return 10
So the basic idea is to get 10 only on the 3rd call of "generate" function.
My first approach was to use "patch" technique, so I did:
def my_generate_hash():
return 99
with patch.object(Test, 'generate', staticmethod(my_generate_hash)):
print "Got %d" % check_hash()
Buuut I was unable to implement attribute "count" and use it in overriding method (
Second thought was to "Mock" something! So..
mock = MagicMock(Test)
mock.count = 1
def my_generate_hash():
if Test2.count < 3:
Test2.count += 1
return 1
else:
return 10
mock.generate = my_generate_hash
with patch('__main__.Test', mock):
print Test.generate()
But in real case I have other methods in "Test" class, so it won't work.
I'm stuck. Any help will be appreciated!
It might be simpler to subclass the original Test class for use in your tests:
class Test(object):
#staticmethod
def generate():
return 10
class PatchedTest(Test):
count = 1
#staticmethod
def generate():
if Test.count < 3:
Test.count += 1
return 1
else:
return 10
The replacement function could also be done in two somewhat better ways, both of which should make it a lot easier to patch the Test class in the same way you were trying in your question:
Use a #classmethod, allowing the function to access the class it's assigned to:
class PatchedTest(Test):
count = 1
#classmethod
def generate(cls):
if cls.count < 3:
cls.count += 1
return 1
else:
return 10
Use a generator instead - each time the function is called it will continue execution where it last left off. However, this will only work if you are iterating over the functions result:
def alternative_generate():
yield 1
yield 1
yield 10
Looks like in can be in a different way.
self.count = 0
def generate():
if self.count < 3
self.count += 1
return 10
else:
return 99
with patch.object(Test, 'generate', generate):
self.assertEqual(Test.generate(), 10)
self.assertEqual(Test.generate(), 10)
self.assertEqual(Test.generate(), 10)
self.assertEqual(Test.generate(), 99)
self.assertEqual(Test.generate(), 99)

Categories