Very simple question here, but I am new to python as an object oriented programming language. I am trying to write a class. Imagine it is organized as follows:
class myClass:
def __init__(self,a,b,runit=True):
self.a = a
self.b = b
if runit:
self.run_func()
def run_func(self):
self.c = self.a*self.b
return
So as you can see the class is initialized with just a and b. It defaults to initialize c from those arguments, but it need not. Now let me illustrate three use cases that I think should behave the same, but are not:
# Use 1
test = myClass(5,2)
print(test.c)
# Use 2
test = myClass(5,2,runit=False)
test.run_func()
print(test.c)
# Use 3
test = myClass(5,2,runit=False).run_func()
print(test.c)
This returns the following:
10
10
Traceback (most recent call last):
File "<ipython-input-37-cb854baa3a0c>", line 23, in <module>
print(test.c)
AttributeError: 'NoneType' object has no attribute 'c'
Why can the instantiated class not be operated on immediately and pipe this result to test in one step? In my head, (1) and (2) are the same set of operations except one is broken into two steps and the other is done in one line.
And, more importantly, how can I fix this by editing my class to behave in the expected manner?
at # Use 3 myClass(5,2,runit=False).run_func() returns None
to fix you could return self:
def run_func(self):
self.c = self.a*self.b
return self
test = myClass(5,2,runit=False).run_func()
print(test.c)
output:
10
or you should not set your flag runit to False and use:
class myClass:
def __init__(self,a,b,runit=True):
self.a = a
self.b = b
if runit:
self.run_func()
def run_func(self):
self.c = self.a*self.b
test = myClass(5,2)
print(test.c)
output:
10
run_func should return self:
def run_func(self):
self.c = self.a*self.b
return self
stating return without a value following it, is the same as writing return None.
You get this exception because None has no attribute named c.
In the 3rd case, test is assigned the return value of run_func(). It has a bare return which returns None, so test = None.
In case 3
test = myClass(5,2,runit=False).run_func()
print(test.c)
test is not the object, but the return of the run_func(), which is null and indeed has no c attribute
Lets look at the method call in question:
test = myClass(5,2,runit=False).run_func()
Let's break that down. First, you construct an instance of myClass and then you call run_func on that instance. It's the return value of run_func that you assign to test and since run_func doesn't return anything test is None resulting in your error.
As another way to see this, try the following:
class myClass:
def __init__(self,a,b,runit=True):
self.a = a
self.b = b
if runit:
self.run_func()
def run_func(self):
self.c = self.a*self.b
return 11
test = myClass(5,2,runit=False).run_func()
print(test) # will print 11
Related
I have class MyClass in file main.py
class MyClass:
def __init__(self, a=1, b=2, c=3):
self.a = a
self.b = b
self.c = c
self.d = None
self.e = None
self.f = None
def method_1(self):
return self.a + self.b
def method_2(self):
return self.d + 4
def method_3(self):
return self.e + 10
def run(self):
self.d = self.method_1()
self.e = self.method_2()
self.f = self.method_3()
# a lot more stuff here that takes time
and I have the following tests in file test_file.py
import unittest
from unittest.mock import patch
from main import MyClass
class TestMyClass(unittest.TestCase):
def test_method_2_v1(self):
# This test creates a real instance, but the input self.d is created manually in the test,
# in a different way it is done in the production code (in the production code it is done through run())
instance = MyClass()
instance.d = instance.method_1()
instance.e = instance.method_2()
assert instance.e == 7
def test_method_2_v2(self):
# This test creates a real instance, but the input self.d is created manually in the test,
# in a different way it is done in the production code (in the production code it is done through run())
# This case does not run method_1, but gives an explicit value to self.d
instance = MyClass()
instance.d = 3
instance.e = instance.method_2()
assert instance.e == 7
#patch('main.MyClass', spec=True)
def test_method_2_v3(self, my_class_mock):
# This test uses a mock
mock_instance = my_class_mock()
mock_instance.configure_mock(d=3)
assert MyClass.method_2(mock_instance) == 7
I believe the comments and the code clearly explain the differences.
Which one is the best practice and why?
Is there a better solution?
Main difference between the 3 methods
I think that the main difference between the 3 test methods is:
test_method_2_v1() invokes method_1() of MyClass
test_method_2_v2() and test_method_2_v3() don't invoke method_1() of MyClass
This means that test_method_2_v1() executes an indirect test of method_1() while the other 2 test methods execute only a test of method_2().
Comparison between test_method_2_v2() and test_method_2_v3()
Between test_method_2_v2() and test_method_2_v3() I think that the best is test_method_2_v3() because the use of a mock object, when it is possible, is in general preferable because it helps to create unit tests (a mock object is used to simulate the behaviour of an object in a precise test case and not an whole object).
A unit test is a test which verifies a specific functionality. In this case test_method_2_v3() verifies that method_2() returns the value of the attribute d increase of 4.
In my opinion test_method_2_v2() does too many things so it's not a good unit test.
How test_method_2_v2() becomes a unit-test
I think is better to modify test_method_2_v2() and a possible code for it is the following:
def test_method_2_v2(self):
instance = MyClass()
instance.d = 3
# check that method_2 return 'd' increases of 4
assert instance.method_2() == 7
Previous code becomes similar to test_method_2_v3() without using of mock object. Now test_method_2_v2() only verifies the correctness of method_2() (as a test unit must do).
Let's suppose I have a big class with methods that make many references to self. For example:
from unittest import mock
import json
import Gamma
class Alpha:
def __init__(self):
self.a = json.loads('file_A')
self.b = json.loads('file_B')
self.c = None
self.d = None
self.e = Gamma()
def foo(self):
json.dumps(self.d)
def bar(self, dummy):
self.c = dummy.x
self.d = dummy.y
self.foo()
class Beta:
def __init__(self):
self.x = json.loads('file_X')
self.y = json.loads('file_Y')
Let's say I want to test the bar method in the above example utilizing mock. This is what I'm trying but I'm obviously missing something here:
#mock.patch('Beta', autospec=True)
#mock.patch('Alpha', autospec=True)
def test_bar(mock_alpha, mock_beta):
# Set mock values so the Beta class gets instantiated properly
mock_beta.x = 8
mock_beta.y = 9
# Call the bar method from mocked Alpha class
mock_alpha.bar(mock_beta)
# Test whether bar method updated the Alpha class as desired
assert mock_alpha.c == 8
assert mock_alpha.foo.called
These are the errors I get for the two asserts:
> raise AttributeError("Mock object has no attribute %r" % name)
E AttributeError: Mock object has no attribute 'c'
> assert mock_alpha.foo.called
E AssertionError: assert False
E + where False = <MagicMock name='Alpha.foo' spec='function' id='5324111568'>.called
E + where <MagicMock name='Alpha.foo' spec='function' id='5324111568'> = <MagicMock name='Alpha' spec='Alpha' id='5323992784'>.foo
How do I go about testing everything that foo does using mock?
PS: This is a stripped down example; it might make more sense to mock json object here in this particular example. However, in my real use case, the Alpha class is very complicated and would be a lot of work to mock all the endpoints. My question is confined to whether there's a way to mock instance of a class the way I'm attempting (unsuccessfully) to do in the example.
I will patch json.loads() method instead of patching Beta class. Besides, I patched .foo() method of Alpha class.
E.g.
main.py:
import json
class Alpha:
def __init__(self):
self.a = json.loads('file_A')
self.b = json.loads('file_B')
self.c = None
self.d = None
def foo(self):
json.dumps(self.d)
def bar(self, dummy):
self.c = dummy.x
self.d = dummy.y
self.foo()
class Beta:
def __init__(self):
self.x = json.loads('file_X')
self.y = json.loads('file_Y')
test_main.py:
from unittest import mock
import unittest
from main import Alpha, Beta
class TestAlpha(unittest.TestCase):
#mock.patch('main.json.loads')
#mock.patch('main.Alpha.foo')
def test_bar(self, mocked_foo, mocked_json_loads):
def json_loads_side_effect(s):
if s == 'file_X':
return 'a'
if s == 'file_Y':
return 'b'
mocked_json_loads.side_effect = json_loads_side_effect
alpha = Alpha()
beta = Beta()
alpha.bar(beta)
self.assertEqual(alpha.c, 'a')
self.assertEqual(alpha.d, 'b')
mocked_foo.assert_called_once()
if __name__ == '__main__':
unittest.main()
test result:
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
Name Stmts Miss Cover Missing
-----------------------------------------------------------------------
src/stackoverflow/68402729/main.py 17 1 94% 12
src/stackoverflow/68402729/test_main.py 21 0 100%
-----------------------------------------------------------------------
TOTAL 38 1 97%
After researching this for a while and considering #slideshowp2 's answer to this question. I have found a solution to be most close to what I was looking for. It seems like my initial idea of mocking entire class Alpha is unfeasible, instead I can mock any endpoints of Alpha but will need to have Alpha instantiated one way or another.
Here's my final solution that works.
#pytest.fixture
def set_up_alpha():
"""
Use this fixture to set-up an instance of Alpha object
Mock any endpoints as necessary
"""
with mock.patch.object(json, 'loads') as mock_json_loads:
mock_json_loads.side_effect = lambda x: "test_string"
a = Alpha()
yield a
#mock.patch.object(Alpha, 'foo', autospec=True)
def test_bar(mock_foo, set_up_alpha):
"""
GIVEN an Alpha object
AND a mocked Beta object
WHEN the bar method is invoked
THEN corresponding instances of Alpha object are updated
AND method foo is called
AND the method does not return anything
"""
# Use a mocked object for Beta instance
class MockBeta:
x = "test_x"
y = "test_y"
# Instantiate an Alpha object
a = set_up_alpha
output = a.bar(MockBeta)
# Test whether bar method updated the Alpha class as desired
assert a.c == "test_x"
assert a.d == "test_y"
assert mock_foo.called
assert output is None
I have a class with several methods. Outputs of a method are used in other methods. I don't want to pass these variables as input argument to other methods (to make code more simple).
I can add output of this method into self so I have access to these variables in other methods.
But, I want to be sure that it is a standard implementation. I am worried that it may cause unpredictable error. If you have experience in this regard, please let me know if the following example is a correct implementation or not.
class MyClass:
def method_1(self, A):
return A + 1
def method_2(self):
return self.B + 10
def method_3(self, C):
self.B = self.method_1(C)
result = self.method_2()
return result
z = MyClass()
z.method_3(1)
In the above example, I don't need to pass self.B into method_2. This code works but I want to be sure that it is a standard way.
The real program I working on is complicated, so I made a simple example for this question.
Yup it is more or less correct but the standard way of doing something like this is having a __init__() method and using function annotations.
class MyClass:
def __init__(self) -> None:
self.B = 0
def method_1(self, A: int) -> int:
return A + 1
def method_2(self) -> int:
return self.B + 10
def method_3(self, C: int) -> int:
self.B = self.method_1(C)
result = self.method_2()
return result
z = MyClass()
z.method_3(1)
Where method_2() relies on an attribute that may be unset, make it private so that people aren't tempted to use it. For example, what if I did this?
>>> z = MyClass()
>>> z.method_2()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "tmp.py", line 9, in method_2
return self.B + 10
AttributeError: 'MyClass' object has no attribute 'B'
For that matter, it's probably best to make the attribute private too. So:
class MyClass:
def method_1(self, A):
return A + 1
def _method_2(self):
return self._B + 10
def method_3(self, C):
self._B = self.method_1(C)
result = self._method_2()
return result
By the way, where method_1() doesn't use self, consider making it a staticmethod.
A noob confused question,
I have two methods in a class as :
from example import sample2
class sample1:
def m1():
a='apple'
b='ball'
return sample2.m3(a,b)
def m2():
a='ant'
b='bat'
c='cat'
return sample2.m3(a,b,c)
in example.py:
class sample2:
def m3("here I want to access any `a`,`b`,`c` of respective m1 and m2"):
.....
Iam sorry if this question makes no sense, but when I try to access only this as:
class sample1:
def m1():
a='apple'
b='ball'
return sample2.m3(a,b)
in example.py:
class sample2:
def m3(a,b):
print(a)
a has value apple, so similar way why can't I access any value of a,b,c from that particular m1,m2 returned ?
This is how you use decorators. More information how decorator works can be found in for example here: https://www.datacamp.com/community/tutorials/decorators-python
I would suggest you to first try to better understand concept of class and objects. Example tutorial: https://www.w3schools.com/python/python_classes.asp
This post could also help you to understand how staticmethod decorator works - What is the difference between #staticmethod and #classmethod?
from example import sample2
class sample1:
#staticmethod
def m1():
a='apple'
b='ball'
return sample2.m3(a,b)
#staticmethod
def m2():
a='ant'
b='bat'
c='cat'
return sample2.m3(a,b,c)
example.py file with explanation:
class sample2:
#staticmethod
def m3(a, b, c=None): # it works exactly the same as m3 function that is outside the class
print(a)
# this can be used without creating an object of sample2 class, example:
# sample2.m3(a="apple, b="ball")
def m3_method(self, a, b): # this one requires object on which it can be called
print(a)
# you have access to sample2 class object via self parameter, example of code:
# sample2_object = sample2() # you create object of sample2 class here
# sample2_object.m3_method(a="apple", b="ball") # you call m3_method on sample2_object here
def m3(a, b, c=None): # default value of c is add so you can either call it with 2 or 3 arguments
# example calls:
# m3("a", "b")
# m3("a", "b", "c")
print(a)
You should be able to run this code and I think it gives you an idea how Python classes can be used.
Variables in Python always apply to a specific scope, such as a class, function or closure. Python uses lexical scoping, which means scopes are only connected by nesting in the source code. Most importantly, variables in different scopes are not connected at all.
When you "pass a variable" to a function, you are actually passing only the value around. The variable does not exist in other functions (unless they are nested) nor the surrounding scope.
def nested(a):
a = 3
print('a =', a) # a = 3
def parent():
a = 4
nested(a)
print('a =', a) # a = 4
parent()
print(a) # NameError: name 'a' is not defined
Functions should primarily exchange data by calling with input and returning results:
def nested(a): # receive input
a = 3
print('a =', a) # a = 3
return a # return output
def parent():
a = 4
a = nested(a) # replace a with result of nested(a)
print('a =', a) # a = 3
parent()
Note that only values are passed in and returned. The above could would behave exactly the same if you renamed a in either function.
When working with class instances, the instance itself works as a namespace (similar to a scope). Methods of that instance can exchange data by modifying attributes of the instance. The instance is always passed as the first argument to methods:
class Example():
"""An example for setting attributes on an instance"""
def __init__(self):
self.a = 0
def nested(self):
self.a = 3
print('self.a =', self.a) # self.a = 3
def parent(self):
self.a = 4
print('self.a =', self.a) # self.a = 4
self._nested()
print('self.a =', self.a) # self.a = 3
instance = Example()
print(instance.a) # 0
instance.parent() # self.a = 4
# self.a = 3
To exchange data between objects, methods should also primarily exchange data by calling with input and returning results :
class Example():
"""An example for setting attributes on an instance"""
def __init__(self, a):
self.a = a
def multiply(self, value):
return self.a * value
instance = Example(6)
print(instance.multiply(10)) # 60
I'd like to create a class that has 2 input attributes and 1 output attribute such that whenever one of the input attributes are modified the output attribute is modified automatically
I've tried defining the attributes as instance variables within and outside the constructor function but in either case, after instantiating the object, the output attribute remains fixed at the value set at the moment of instantiation
class Example():
def __init__(self,n):
self.name=n
inA=1
inB=1
if inA==1 and inB==1:
outA=1
else:
outA=0
when instantiated outA is set to 1 as expected
but if I try to update:
object.inA=0
object.outA remains 1 whereas I need it to be updated to 0
Trying to avoid the use of functions if possible. New to python and OOP so sorry if this question is nonsensical or has an obvious answer
If you want instance attributes that depend on other instance attributes, properties are the way to go.
class Example:
def __init__(self, n):
self.name = n
self.inA = 1
self.inB = 1
#property
def outA(self):
return self.inA and self.inB
You access outA like a regular instance attribute, obj.outA.
>>> my_obj = Example("example")
>>> my_obj.outA
1
Changing the attributes inA and inB affect outA.
>>> my_obj.inA = 0
>>> my_obj.outA
0
You can create a function in the class and some other minor changes:
class Example():
def __init__(self,n):
self.name=n
self.inA=1
self.inB=1
def f(self):
if self.inA==1 and self.inB==1:
self.outA=1
else:
self.outA=0
To call it:
a = Example('foo')
a.inA = 0
a.f()
print(a.outA)
Output:
0
As you can see, taking out:
a.f()
line would make it give an error:
AttributeError: 'Example' object has no attribute 'outA'
Do you want it to return your output?
Expanding on U9-Forward's answer:
class Example():
def __init__(self,n):
self.name = n
self.inA = 1
self.inB = 1
def f(self):
return self.inA and self.inB