I have a really long nested list, that looks something like this
['a','b',['c','d',['e',['a','b',['c','d',['e',['a','b',['c','d',['e4',['a','b',['c','d',['e',['a','b',['c','d',['e',['a','b',['c','d',['e14']]]]]],]]]]]]]]],]]]
I'm trying to find value inside that list, however in my case a normal for loops doesn't works because i need to loop through each one and that would take so long.
I came up with this recursive function:
def locate(seq :list, value:int):
for item in seq:
if item.__class__ is list:
locate(item, value)
else:
if(item == value):
return True
return False
So here is the unit test that i created for my algorithm:
import unittest
class TestCase(unittest.TestCase):
def test_locate(self):
self.assertEqual(locate(['a','b',['c','d',['e']]],'e'), True)
def test_locate_two(self):
self.assertEqual(locate(['a','b',['c','d',['e',['a','b',['c','d',['e',['a','b',['c','d',['e4',['a','b',['c','d',['e',['a','b',['c','d',['e',['a','b',['c','d',['e14']]]]]],]]]]]]]]],]]],'e'), True)
if __name__ == '__main__':
unittest.main()
Test result:
Ran 2 tests in 0.001s
FAILED (failures=2)
So, I tried this modification:
def locate(seq :list, value:int):
el_check = False
for item in seq:
if item.__class__ is list:
el_check = el_check or locate(item, value)
else:
if(item == value):
return True
return el_check
Seems to work in couple tests, if you find a case it does not work at, please post so I can try to have a look at it.
Related
I want to be able to mock a function that mutates an argument, and that it's mutation is relevant in order for the code to continue executing correctly.
Consider the following code:
def mutate_my_dict(mutable_dict):
if os.path.exists("a.txt"):
mutable_dict["new_key"] = "new_value"
return True
def function_under_test():
my_dict = {"key": "value"}
if mutate_my_dict(my_dict):
return my_dict["new_key"]
return "No Key"
def test_function_under_test():
with patch("stack_over_flow.mutate_my_dict") as mutate_my_dict_mock:
mutate_my_dict_mock.return_value = True
result = function_under_test()
assert result == "new_value"
**Please understand i know i can just mock os.path.exists in this case but this is just an example. I intentionally want to mock the function and not the external module.
**
I also read the docs here:
https://docs.python.org/3/library/unittest.mock-examples.html#coping-with-mutable-arguments
But it doesn't seem to fit in my case.
This is the test i've written so far, but it obviously doesn't work since the key changes:
def test_function_under_test():
with patch("stack_over_flow.mutate_my_dict") as mutate_my_dict_mock:
mutate_my_dict_mock.return_value = True
result = function_under_test()
assert result == "new_value"
Thanks in advance for all of your time :)
With the help of Peter i managed to come up with this final test:
def mock_mutate_my_dict(my_dict):
my_dict["new_key"] = "new_value"
return True
def test_function_under_test():
with patch("stack_over_flow.mutate_my_dict") as mutate_my_dict_mock:
mutate_my_dict_mock.side_effect = mock_mutate_my_dict
result = function_under_test()
assert result == "new_value"
How it works is that with a side effect you can run a function instead of the intended function.
In this function you need to both change all of the mutating arguments and return the value returned.
easiest way to explain this one:
import unittest
from element import Element
class TestHTMLGen(unittest.TestCase):
def test_Element(self):
page = Element("html", el_id=False)
self.assertEqual(page, Element("html", el_id=False)) # this is where I need help
I get the following error:
AssertionError: <element.Element object at 0x025C1B70> != <element.Element object at 0x025C1CB0>
I know the objects are not exactly the same but is there any way to check that they are equal? I would think that assertEqual would work.
edit: I am working with the addTypeEqualityFunc. However, I am still having trouble
def test_Element(self):
page = Element("html", el_id=False)
self.addTypeEqualityFunc(Element, self.are_elements_equal)
self.assertEqual(page, Element("html", el_id=False))
def are_elements_equal(self, first_element, second_element, msg=None):
print first_element.attribute == second_element.attribute
return type(first_element) is type(second_element) and first_element.tag == second_element.tag and first_element.attribute == second_element.attribute
This is the output I get:
False
and it says the test passed. It should not pass because first_element.attribute is not equal to second_element.attribute. Furthermore, even if I just have return false for are_elements_equal, the test still passes.
Solution:
import unittest
from element import Element
class TestHTMLGen(unittest.TestCase):
def test_Element(self):
page = Element("html", el_id=False)
self.addTypeEqualityFunc(Element, self.are_elements_equal)
self.assertEqual(page, Element("html", el_id=False)) # this is where I need help
def are_elements_equal(self, first_element, second_element, msg=None):
self.assertEqual(type(first_element), type(second_element))
self.assertEqual(first_element.tag, second_element.tag)
self.assertEqual(first_element.attribute, second_element.attribute)
however, a lot of times self.assertEqual(vars(page), vars(Element("html", el_id=False))) will do the trick
edit: also, I should add. I made a cool little function that can check if objects are equal. Should work in most cases.
def are_elements_equal(self, first_element, second_element, msg=None):
self.assertEqual(type(first_element), type(second_element))
try:
type(vars(first_element)) is dict
except:
self.assertEqual(first_element, second_element)
else:
for i in vars(first_element).keys():
try:
type(vars(vars(first_element)[i])) is dict
except:
if type(vars(first_element)[i]) is list:
for j in range(len(vars(first_element)[i])):
self.are_elements_equal(vars(first_element)[i][j], vars(second_element)[i][j])
else:
self.assertEqual(vars(first_element)[i], vars(second_element)[i])
else:
self.are_elements_equal(vars(first_element)[i], vars(second_element)[i])
Use vars():
Return the dict attribute for a module, class, instance, or any other object with a dict attribute.
self.assertEqual(vars(page), vars(Element("html", el_id=False)))
For example, I have a self defined class, like this:
class Alarm(object):
def __init__(self, alarmId, msg):
self.alarmId = alarmId
self.msg = msg
def __eq__(self, other):
return self.alarmId == other.alarmId
aList = list()
a = Alarm(1, "hello")
b = Alarm(1, "good")
aList.append(a)
aList.append(b)
Alarms with same Id are considered same, so "a" and "b" is actually same. I want to check if same Alarm already exist in the list, if it already existed , then no need to add it to the list.
if a in aList: # I wish when this "in" called, I could call one member function of a to match the whole list
pass
But which function do I need to overwrite to do this? I tried __eq__, but it could not accomplish what I want.
I think is what you are after (assuming you compering using self.alarmId):
class Alarm(object):
def __init__(self, alarmId, msg):
self.alarmId = alarmId
self.msg = msg
def __eq__(self, other):
return (isinstance(other, self.__class__)
and self.alarmId == other.alarmId)
aList = list()
a = Alarm(1, "hello")
b = Alarm(2, "good")
aList.append(a)
aList.append(b)
if a in aList:
print("a found")
c = Alarm(3, "good")
if c not in aList:
print("c not found")
Result is:
a found
c not found
Deducing from your question, I think you are looking for a conditionall append() function for your list. This would then be something like the following:
def listadd(list, toadd):
for alarm in list:
if alarm == toadd:
return false
list.append(toadd)
return true
You could use this function to add alarms to your list and it checks right away if the alarm is in the list. This is obviously not an overloaded function or operator but it should work.
Hope it helps.
EDIT:
You can call the function with the list you want to add to and the item you want to add. It returns a boolean flag if the item was added or not.
I'm currently trying to run a number of tests against a JSON string there are however a few difficulties that I am encountering.
Here's what I have so far.
class PinpyTests(jsonstr, campaign):
data = json.loads(jsonstr)
test = False
def dwellTest(self):
if self.data.get('dwellTime', None) is not None:
if self.data.get('dwellTime') >= self.campaign.needed_dwellTime:
# Result matches, dwell time test passed.
self.test = True
def proximityTest(self):
if self.data.get('proximity', None) is not None:
if self.data.get('proximity') == self.campaign.needed_proximity:
# Result matches, proximity passed.
self.test = True
Basically, I need the tests to be run, only if they exist in the json string. so if proximity is present in the string, it will run the proximity test, etc etc. (there could be more tests, not just these two)
The issue seems to arise when both tests are present, and need to both return true. If they both return true then the test has passed and the class can return true, However, if dwell fails, and proximity passes I still need it to fail because not all the tests pass. (where proximity makes it pass). I'm slightly baffled as how to continue.
For starters, your class is defined incorrectly. What you probably want is an __init__ function. To achieve your desired result, I would suggest adding a testAll method that checks for each test in your json then runs that test.
class PinpyTests(Object):
test = False
def __init__(self, jsonstr, campaign):
self.data = json.loads(jsonstr)
self.campaign = campaign
def testAll(self):
passed = True
if self.data.get('dwellTime') is not None:
passed = passed and self.dwellTest()
if self.data.get('proximity') is not None:
passed = passed and self.proximityTest()
return passed
def dwellTest(self):
if self.data.get('dwellTime') >= self.campaign.needed_dwellTime:
# Result matches, dwell time test passed.
return True
return False
def proximityTest(self):
if self.data.get('proximity') == self.campaign.needed_proximity:
# Result matches, proximity passed.
return True
return False
So I have a long chain of conditions that should be validated to be true. Instead of chaining a long if condition, I tried to be "innovative" and did it this way, which I reckon is more readable. But my question is, is this the optimal way of doing it?
Or is there a pythonic way of doing it? PS: Please respond with an alternative instead of answering "No", thanks!
Here's the code chunk:
def site_exists(site):
"""
returns the sitebean if it exists,
else returns false
"""
vpadmin_service = _get_vpadmin_service(site)
all_sites = VpAdminServiceUtil.getSites(vpadmin_service)
for site_listing in all_sites:
if site.getId():
#condition check
try:
assert site.getId() == site_listing.getId()
assert site.getName() == site_listing.getName()
assert site.getCustomer().getId() == site_listing.getCustomer().getId()
except AssertionError:
continue
#pass conditions
return site_listing
#no id, so just check for name and customer
else:
#condition check
try:
assert site.getName() == site_listing.getName()
assert site.getCustomer().getId() == site_listing.getCustomer().getId()
except AssertionError:
continue
#pass conditions
site.setId(site_listing.getId())
return site_listing
return False
A simpler approach is to build a tuple of the conditions and compare the tuples:
def site_info(s):
return s.getId(), s.getName(), s.getCustomer().getId()
if site_info(site) == site_info(site_listing):
return site_listing
else:
continue
If you have a lot of conditions, or the conditions are expensive, you can instead create a generator for the conditions, and compare with any or all:
import itertools
def iter_site_info(s):
yield s.getId()
yield s.getName()
yield s.getCustomer().getId()
if all(x==y for (x, y) in itertools.izip(iter_site_info(site), iter_site_info(site_listing)):
return site_listing
else:
continue
I'm not sure whether Jython has any and all, but they're trivial functions to write.
EDIT - any and all appeared in Python 2.5, so Jython should have them.
Using exception for control flow is bad! And it will also kill your performance. Assuming the condition will be true in one out of 10 cases? So 9 exceptions to handle in the worst case - this comes with a huge performance cost.
If you want readability, try:
if (
condition1 and \
condition2 and \
condition3
):
# do something
For the revised version, this should be equivalent:
for site_listing in all_sites:
if site.getName() == site_listing.getName() and site.getCustomer().getId() == site_listing.getCustomer().getId():
if not site.getId():
site.setId(site_listing.getId())
if site.getId() == site_listing.getId():
return site_listing
I would implement an is_same method on site and call it in the for loops.
all_sites = VpAdminServiceUtil.getSites(vpadmin_service)
for site_listing in all_sites:
if site_listing.is_same(site, set_id=True):
return site_listing
As for its implementation (that is your real question), I suggest something like:
...
if self.getName() != site.getName(): return False
if self.getCustomer() != site.getCustomer(): return False
...
return True
Use the __eq__ method! If you wrote the class of site yourself, no problem. If it is from a module outside of your control, see this solution, or create a somewhat less elegant wrapping class. For the wrap variant, this would be the class:
class WrappedSite(object):
def __init__(self, site):
self.site = site
def __eq__(self, other):
if site.getId():
if self.site.getId() != other.site.getId():
return False
if self.site.getName() != other.site.getName():
return False
if self.site.getCustomer().getId() != other.site.getCustomer().getId():
return False
return True
Then your big ugly function is reduced to this:
def site_exists(site):
"""
returns the sitebean if it exists,
else returns false
"""
vpadmin_service = _get_vpadmin_service(site)
all_sites = VpAdminServiceUtil.getSites(vpadmin_service)
wsite = WrappedSite(site)
for site_listing in all_sites:
if wsite == WrappedSite(site_listing):
return site_listing
return False
EDIT Fixed site_exists to return sitebean instead of True.
Remove some code duplication:
def site_exists(site):
"""
returns the sitebean if it exists,
else returns None
"""
vpadmin_service = _get_vpadmin_service(site)
all_sites = VpAdminServiceUtil.getSites(vpadmin_service)
for site_listing in all_sites:
if (site.getName() == site_listing.getName() and
site.getCustomer().getId() == site_listing.getCustomer().getId()):
if site.getId(): # if id is set; it should be the same
if site.getId() != site_listing.getId(): continue
else: # no id; consider it the same site
site.setId(site_listing.getId()) #XXX side-effect
return site_listing
Note: it is unexpected that site_exists() might modify its argument (via .setId()). Consider to refactor it:
def same_site(site, other):
if site.getId() and site.getId() != other.getId():
# if id is set; it should be the same
return False
return (site.getName() == other.getName() and
site.getCustomer().getId() == other.getCustomer().getId())
def get_site_listing(site):
"""
returns the sitebean corresponding to `site`,
returns None if there is none
"""
vpadmin_service = _get_vpadmin_service(site)
all_sites = VpAdminServiceUtil.getSites(vpadmin_service)
return next((s for s in all_sites if same_site(site, s)), None)
Note: the code doesn't modify the site object. Use the return value from get_site_listing() instead.
If next() is unavailable then use:
for site_listing in all_sites:
if same_site(site, site_listing):
return site_listing
return None
btw, jython should provide property wrappers for you; so you could write:
def same_site(site, other):
if site.id and site.id != other.id:
# if id is set; it should be the same
return False
return site.name == other.name and site.customer.id == other.customer.id
and site.id = id instead of site.setId(id).
Using an exception handler as a code target is basically a "goto". I don't see anything wrong with it really (despite the ancient furor surrounding "goto considered harmful"), but knowing that it's a goto might give you a different perspective.
Using exception handling for control flow is pythonic in the sense that "it's easier to ask for forgiveness than for permission". However, that doesn't extend to making rules to break just so that you can ask for forgiveness.
Others have provided excellent alternatives. Just wanted to address the principle aspect.