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)
Related
I'm trying to write a mixin that increments a counter every time a public attribute is read. My implementation below seems to have a hidden increment that I cannot find. From the commented out print statements I can identify it as occurring in the self._read_count += 1 statement. What am I misunderstanding?
class CounterMixin:
def __init__(self):
self._read_count = 0;
super().__init__();
def _inc_read_count(self):
# print(f'Pre-inc: {self._read_count}');
self._read_count += 1;
# print(f'Post-inc: {self._read_count}');
#property
def read_count(self):
self._inc_read_count();
return self._read_count;
def __getattribute__(self, attr):
if attr[0] != '_':
# print(f'Counting {attr}');
self._inc_read_count();
else:
# print(f'Not counting {attr}');
pass;
return super().__getattribute__(attr);
class Bar(CounterMixin):
pass
foo = Bar();
print(foo.read_count);
print('---');
foo.x = 1;
print(foo.read_count);
print('---');
_ = foo.x;
print(foo.read_count);
Expected output:
1
---
2
---
4
Actual output:
2
---
4
---
7
You're incrementing self._read_count twice when you access read_count, once in __getattribute__ and once in the property getter.
I need a little help with my homework:
I have a class course with the course name and grade and class with student name, age, and also in student class should be a list of all courses and their grades.
I want to use only lists. I could to it with dict but is it possible to do it with lists? each course contains name of the course and grade
The code I tried that don't do anything:
class Course:
def __init__(self,coursename,grade):
self.coursename=coursename
self.grade=int(grade)
self.newList=[]
def __str__(self):
return self.coursename+":"+ str(self.grade)
class Student:
def __init__(self,name,age):
self.name=name
self.age=int(age)
self.listCoursenamesAndGrades=[]
#super().__init__(self,coursename,grade)
def addCourse(self,c):
self.listCoursenamesAndGrades.append(c)
def __iter__(self):
self._current = 0
return self
def __next__(self):
try:
while True:
c=self.listCoursenamesAndGrades[self.current]
if (self.current <len(self.listCoursenamesAndGrades)):
self.current+=1
if c.grade > 59:
return c
except IndexError:
raise StopIteration
output:
s1=Student("Sam",24)
C1=Course("js",80)
s1.addCourse(Course("Math",85))
s1.addCourse(Course("Math",70))
for c in s1:
print(c)
and the output will be:
Js:80
Math:85
Math:70
thank you for the help!!
The problem is that __next__ should return a single item; it should not be iterating over a list. For what you want, it would be simpler to put the iteration logic in __iter__ to start:
def __iter__(self):
for c in self.listCoursenamesAndGrades:
if c.grade > 59:
yield c
Since __iter__ doesn't return self itself, you can have multiple independent iterators (just don't be modifying the course list in the mean time).
As an example of making Student an iterator with its own separate __next__ method, you need to store the current state of iteration on the Student itself. For example:
# Since _current is an attribute of the Student instance itself,
# you can't have multiple independent iterators. Something like
#
# i1 = iter(Student)
# print(next(i1))
# i2 = iter(Student)
# print(next(i1)
#
# will print the same course twice; calling `iter(Student)` resets
# the object's iteration state.
def __iter__(self):
self._current = 0
return self
def __next__(self):
try:
while True:
c = self.listCoursenamesAndGrades[self._current]
self._current += 1
if c.grade > 59:
return c
except IndexError:
raise StopIteration
I am currently having an issue, as i am relatively new to python , it might be a very easy solution for others.
I want to pass a parameter between both functions 'eg1' and 'eg2', there is a common number the user will input (example:10) then 'eg1' will add 1 to it and 'eg2' will take the final value of 'eg1' and add 1 more to it, (example: 10 will become 11 then 12)
It is troubling me because this keeps popping up:
Traceback (most recent call last):
File "example.py", line 69, in <module>
eg2(a)
File "example.py", line 63, in eg2
b = a.d
AttributeError: 'int' object has no attribute 'd'
I can't seem to find my mistake.
class Helper:pass
a = Helper()
def one(a):
d = a
d += 1
print d
def two(a):
b = a.d
b += 1
print b
print
print ("Please enter a number.")
a = int(input('>> ')
print
one(a)
print
two(a)
Reference for parameter passing:
Python definition function parameter passing
'Print' with nothing means to leave an empty line, for me
I messed up the title, fixed.
Since you are already using a class, you can pass the number you want to increment twice as an instance attribute, and call your increment functions on that attribute. This will avoid passing the updated value after calling one in the method two
Calling one and then two makes sure that two is working on the updated value after calling one.
class Helper:
# Pass num as parameter
def __init__(self, num):
self.num = num
# Increment num
def one(self):
self.num += 1
# Increment num
def two(self):
self.num += 1
h = Helper(10)
h.one()
print(h.num) # 11
h.two()
print(h.num) # 12
Based on your comments, here's one way to get the result. I am using python3:
class Helper:
d = None
cl = Helper()
def one(a):
cl.d = a
cl.d += 1
return cl.d
def two():
cl.d += 1
return cl.d
print ("Please enter a number.")
a = int(input('>> '))
print(one(a))
print(two())
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")
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