Search in the list - python

class CommandManager:
def __init__(self):
self.commands = []
def add_command(self, command: Command):
if command is None:
return None
for target in self.commands:
if target.name is command.name:
return None
print(command.name, command.description, command.usage, command.min_arguments) # debug
self.commands.append(command)
return command
def get_command(self, name):
if name is None:
return None
for target in self.commands:
if name is target.name:
return target
return None
What's wrong with this code? Adding to the array and looking for it in the add_command method works fine, but inget_command it does not find it. No value is None.

is tests for identity, not equality. That means Python simply compares the memory address a object resides in.
you should use == operator to test equality of a string like:
if name == target.name:

is operator tests if two variables point to the same object. Use == instead.
if name == target.name:

Related

How can I accept one variable or multple when using a function?

I'm looking to create a search function similar to my rent_book fuction that allows me to search by first name, second name or title or any combination of the three. So I could maybe search for first name "George" and title "Animal Farm" or just title "Animal Farm" and receive the same result.
Books are stored in a list of dictionaries this is the dict struct and the rent_book function. I could do a convoluted nest of ifs but I'm sure there's a better way.
book = {
"fname": fname,
"sname": sname,
"title": title,
"avail": True
}
def rent_book(self, fname, sname, title):
# if is_return is False:
for x in self.lstBooks:
if x['fname'] == fname and x['sname'] == sname and x['title'] == title and x['avail'] is True:
x['avail'] = False
return True
return False
Thanks
Since your arguments are the same as the dictionary keys you're matching, you could just use **kwargs and iterate over the kwargs:
def rent_book(self, **kwargs):
# if is_return:
# return False
if not kwargs:
raise KeyError("Must search on at least one of fname, sname, or title.")
for x in self.lstBooks:
if not (x['avail'] and all(x[k] == v for k, v in kwargs.items())):
continue
x['avail'] = False
return True
return False
Note that the function will implicitly raise KeyError if it's called with any invalid keys (the x[k] will raise it), and there's an explicit raise KeyError to guard against the caller accidentally not providing any kwargs at all, since otherwise it would just return the first book in lstBooks.
(Yes, pedants, they can still call it with avail=True.)
I am not sure If I understood Your question correctly,
But If I am correct, You want the function to work appropriately handling the following cases:
if all "title","fname","sname" are given.
if only one among the 3 is given.
If any pair of 2 among the 3 is given.
If that is the case You can use default Parameters in the function for all the 3.
def rent_book(self, fname="", sname="", title=""):
# if is_return is False:
for x in self.lstBooks:
if (len(fname) and x['fname'] == fname) and (len(sname) and x['sname'] == sname) and (len(title) and x['title'] == title) and x['avail'] is True:
x['avail'] = False
return True
return False
The line
(len(fname) and x['fname'] == fname)
makes sure that You only check the condition if fname is explicitly provided in as parameter.
So, In all the above scenarios mentioned above,
You can call the same function with only optional parameters
(Dont forget to specify the parameter name while calling else it will always consider it the value for the first parameter by default!!)
The first thing to do here is think about having a Book class. Initially it needs to support sname, fname, title and avail (a flag to indicate whether or not the book is available to rent).
We could then construct a Book instance with any combination of these attributes or even none.
So let's start with this:
class Book:
klist = ['sname', 'fname', 'title']
def __init__(self, **kwargs):
self.vars = kwargs
for k in Book.klist:
self.vars.setdefault(k, None)
self.vars.setdefault('avail', True)
#property
def avail(self):
return self['avail']
#avail.setter
def avail(self, v):
self.vars['avail'] = v
def __getitem__(self, item):
return self.vars.get(item, None)
def __repr__(self):
return f'sname={self["sname"]} fname={self["fname"]} title={self["title"]} avail={self.avail}'
Defining getitem keeps the code neater and safer. The repr is optional - it just helps to see what's going on should we ever want to print an instance of this class.
Let's create a couple of Book instances and put them in a list.
booklist = [Book(sname='Orwell', fname='George', title='Animal Farm'), Book(sname='Fleming', fname='Ian', title='Casino Royale')]
Now we want to rent a book. We can provide any one or all of the main attributes. If there's a match for all of the attributes that have been passed and if that book is available, we'll mark it as unavailable and return True. If nothing matches or the book isn't available we return False.
def rent_book(**kwargs):
for book in booklist:
if book.avail:
for k, v in kwargs.items():
if book[k] is not None and v != book[k]:
break
else:
book.avail = False
return True
return False
Now let's see what happens:
print(rent_book(title='Casino Royale'))
print(rent_book(title='Casino Royale'))
print(rent_book(sname='Orwell', title='Animal Farm'))
Output:
True # the book title matches and it's available
False # the book title matches but it's no longer available
True # both the surname and title match so it's available

Use of arguments with property.deleter

I'm trying to define a property deleter with a parameter for an attribute of Character class as follows:
class Character(object):
_status = None
#property
def status(self):
""" Return _status if it exists or False if not."""
return self._status
#status.setter
def status(self, status_value):
"""
Receive the status and the duration(continous or not) and add
it for the _status.
"""
if not self._status:
self._status = []
self._status.append(status_value)
#status.deleter
def status(self, status_value):
"""
Delete the specified object from the _status list.
"""
status = [value for value in self._status
if status_value in value.keys()]
if status:
self._status.remove(self._status.index(status[0]))
I'm trying to delete a specific object from the status.
>>>a = Character()
>>>a.status = 'Test'
Would return a list with 1 element:
>>>a.status
['Test']
If i set the status again, the old value persists and new one is added to the list:
>>>a.status = 'Dazed'
>>>a.status
['Test', 'Dazed']
As well I want to delete only a specific value from the list:
>>>del a.status('Dazed')
And the expected result should be:
>>> a.status
['Test']
The problem is that hen I try:
del a.status('Dazed')
The following error occurs:
SyntaxError: can't delete function call
Is there any way to use arguments with a property.deleter?
This is odd behaviour you are trying to create, and would likely trip up users of your class. I certainly wouldn't expect:
self.status = "happy"
to add the new string to an existing list.
As far as I'm aware there is no way to pass an argument to a #property.deleter.
A better approach might be to make the character.status a set (I am assuming that you meant this to be an instance attribute, but this all stands for class attributes too):
class Character(object):
def __init__(self, ..., status=None):
if status is None:
self.status = set()
else:
self.status = set(status)
...
conan = Character(..., status=("happy", "cold"))
conan.status.add("tired")
conan.status.remove("happy")
One advantage of a set is that it prevents duplicates. Also, it provides for very fast membership tests (e.g. if "warm" in conan.status:) and you can find out if two Character instances have any of the same status easily:
if conan.status.intersection(other_character.status):

Setting a function equal to a boolean in python

I'm writing a driver in Python 2.6 and I need it to be reverse compatible with a previous implementation (I don't have access to the source code).
class new_driver ():
def output(self, state):
if state == True:
self.set_ouput_on()
else:
self.set_output_off()
...
The odd thing is that to keep compatibility I have to pass this output using the format
nd = new_driver()
nd.output = True
How do I pass a value in this way?
Edit: To clarify: my "output" function has to receive the value True in this way in order to execute the function self.set_output_on()
Try using the #property decorator:
#property
def output(self):
return self... #not sure how you are tracking output on/off
#output.setter
def output(self, state):
if state:
self.set_output_on()
else:
self.set_output_off()

Workaround for equality of nested functions

I have a nested function that I'm using as a callback in pyglet:
def get_stop_function(stop_key):
def stop_on_key(symbol, _):
if symbol == getattr(pyglet.window.key, stop_key):
pyglet.app.exit()
return stop_on_key
pyglet.window.set_handler('on_key_press', get_stop_function('ENTER'))
But then I run into problems later when I need to reference the nested function again:
pyglet.window.remove_handler('on_key_press', get_stop_function('ENTER'))
This doesn't work because of the way python treats functions:
my_stop_function = get_stop_function('ENTER')
my_stop_function is get_stop_function('ENTER') # False
my_stop_function == get_stop_function('ENTER') # False
Thanks to two similar questions I understand what is going on but I'm not sure what the workaround is for my case. I'm looking through the pyglet source code and it looks like pyglet uses equality to find the handler to remove.
So my final question is: how can I override the inner function's __eq__ method (or some other dunder) so that identical nested functions will be equal?
(Another workaround would be to store a reference to the function myself, but that is duplicating pyglet's job, will get messy with many callbacks, and anyways I'm curious about this question!)
Edit: actually, in the questions I linked above, it's explained that methods have value equality but not reference equality. With nested functions, you don't even get value equality, which is all I need.
Edit2: I will probably accept Bi Rico's answer, but does anyone know why the following doesn't work:
def get_stop_function(stop_key):
def stop_on_key(symbol, _):
if symbol == getattr(pyglet.window.key, stop_key):
pyglet.app.exit()
stop_on_key.__name__ = '__stop_on_' + stop_key + '__'
stop_on_key.__eq__ = lambda x: x.__name__ == '__stop_on_' + stop_key + '__'
return stop_on_key
get_stop_function('ENTER') == get_stop_function('ENTER') # False
get_stop_function('ENTER').__eq__(get_stop_function('ENTER')) # True
You could create a class for your stop functions and define your own comparison method.
class StopFunction(object):
def __init__(self, stop_key):
self.stop_key = stop_key
def __call__(self, symbol, _):
if symbol == getattr(pyglet.window.key, self.stop_key):
pyglet.app.exit()
def __eq__(self, other):
try:
return self.stop_key == other.stop_key
except AttributeError:
return False
StopFunciton('ENTER') == StopFunciton('ENTER')
# True
StopFunciton('ENTER') == StopFunciton('FOO')
# False
the solution is to keep a dictionary containing the generated functions around,
so that when you make the second call, you get the same object as in the first call.
That is, simply build some memoization logic, or use one of the libraries
existing with memoizing decorators:
ALL_FUNCTIONS = {}
def get_stop_function(stop_key):
if not stop_key in ALL_FUNCTIONS:
def stop_on_key(symbol, _):
if symbol == getattr(pyglet.window.key, stop_key):
pyglet.app.exit()
ALL_FUNCTIONS[stop_key] = stop_on_key
else:
stop_on_key = ALL_FUNCTIONS[stop_key]
return stop_on_key
You can generalize Bi Rico's solution to allow wrapping any functions up with some particular equality function pretty easily.
The first problem is defining what the equality function should check. I'm guessing for this case, you want the code to be identical (meaning functions created from the same def statement will be equal, but two functions created from character-for-character copies of the def statement will not), and the closures to be equal (meaning that if you call get_stop_function with two equal but non-identical stop_keys the functions will be equal), and nothing else to be relevant. But that's just a guess, and there are many other possibilities.
Then you just wrap a function the same way you'd wrap any other kind of object; just make sure __call__ is one of the things you delegate:
class EqualFunction(object):
def __init__(self, f):
self.f = f
def __eq__(self, other):
return (self.__code__ == other.__code__ and
all(x.cell_contents == y.cell_contents
for x, y in zip(self.__closure__, other.__closure__)))
def __getattr__(self, attr):
return getattr(self.f, attr)
def __call__(self, *args, **kwargs):
return self.f(*args, **kwargs)
If you want to support other dunder methods that aren't required to go through getattr (I don't think any of them are critical for functions, but I could be wrong…), either do it explicitly (as with __call__) or loop over them and add a generic wrapper to the type for each one.
To use the wrapper:
def make_f(i):
def f():
return i
return EqualFunction(f)
f1 = f(0)
f2 = f(0.0)
assert f1 == f2
Or, notice that EqualFunction actually works as a decorator, which may be more readable.
So, for your code:
def get_stop_function(stop_key):
#EqualFunction
def stop_on_key(symbol, _):
if symbol == getattr(pyglet.window.key, stop_key):
pyglet.app.exit()
return stop_on_key

getting test class instance in nose test object to sync up with instance in decorated generator target

I've been working on a way to get tests produced from a generator in nose to have descriptions that are customized for the specific iteration being tested. I have something that works, as long as my generator target method never tries to access self from my generator class. I'm seeing that all my generator target instances have a common test class instance while nose is generating a one-offed instance of the test class for each test run from the generator. This is resulting in setUp being run on each test instance nose creates, but never running on the instance the generator target is bound to (of course, the real problem is that I can't see how to bind the nose-created instance to the generator target). Here's the code I'm using to try to figure this all out (yes, I know the decorator would probably be better as a callable class, but nose, at least version 1.2.1 that I have, explicitly checks that tests are either functions or methods, so a callable class won't run at all):
import inspect
def labelable_yielded_case(case):
argspec = inspect.getargspec(case)
if argspec.defaults is not None:
defaults_list = [''] * (len(argspec.args) - len(argspec.defaults)) + argspec.defaults
else:
defaults_list = [''] * len(argspec.args)
argument_defaults_list = zip(argspec.args, defaults_list)
case_wrappers = []
def add_description(wrapper_id, argument_dict):
case_wrappers[wrapper_id].description = case.__doc__.format(**argument_dict)
def case_factory(*factory_args, **factory_kwargs):
def case_wrapper_wrapper():
wrapper_id = len(case_wrappers)
def case_wrapper(*args, **kwargs):
args = factory_args + args
argument_list = []
for argument in argument_defaults_list:
argument_list.append(list(argument))
for index, value in enumerate(args):
argument_list[index][1] = value
argument_dict = dict(argument_list)
argument_dict.update(factory_kwargs)
argument_dict.update(kwargs)
add_description(wrapper_id, argument_dict)
return case(*args, **kwargs)
case_wrappers.append(case_wrapper)
case_wrapper.__name__ = case.__name__
return case_wrapper
return case_wrapper_wrapper()
return case_factory
class TestTest(object):
def __init__(self):
self.data = None
def setUp(self):
print 'setup', self
self.data = (1,2,3)
def test_all(self):
for index, value in enumerate((1,2,3)):
yield self.validate_equality(), index, value
def test_all_again(self):
for index, value in enumerate((1,2,3)):
yield self.validate_equality_again, index, value
#labelable_yielded_case
def validate_equality(self, index, value):
'''element {index} equals {value}'''
print 'test', self
assert self.data[index] == value, 'expected %d got %d' % (value, self.data[index])
def validate_equality_again(self, index, value):
print 'test', self
assert self.data[index] == value, 'expected %d got %d' % (value, self.data[index])
validate_equality_again.description = 'again'
When run through nose, the again tests work just fine, but the set of tests using the decorated generator target all fail because self.data is None (because setUp is never run because the instance of TestTest stored in the closures is not the instances run by nose). I tried making the decorator an instance member of a base class for TestTest, but then nose threw errors about having too few arguments (no self) passed to the unbound labelable_yielded_case. Is there any way I can make this work (short of hacking nose), or am I stuck choosing between either not being able to have the yield target be an instance member or not having per-test labeling for each yielded test?
Fixed it (at least for the case here, though I think I got it for all cases). I had to fiddle with case_wrapper_wrapper and case_wrapper to get the factory to return the wrapped cases attached to the correct class, but not bound to any given instance in any way. I also had another code issue because I was building the argument dict in wrapper wrapper, but then not passing it to the case. Working code:
import inspect
def labelable_yielded_case(case):
argspec = inspect.getargspec(case)
if argspec.defaults is not None:
defaults_list = [''] * (len(argspec.args) - len(argspec.defaults)) + argspec.defaults
else:
defaults_list = [''] * len(argspec.args)
argument_defaults_list = zip(argspec.args, defaults_list)
case_wrappers = []
def add_description(wrapper_id, argument_dict):
case_wrappers[wrapper_id].description = case.__doc__.format(**argument_dict)
def case_factory(*factory_args, **factory_kwargs):
def case_wrapper_wrapper():
wrapper_id = len(case_wrappers)
def case_wrapper(*args, **kwargs):
argument_list = []
for argument in argument_defaults_list:
argument_list.append(list(argument))
for index, value in enumerate(args):
argument_list[index][1] = value
argument_dict = dict(argument_list)
argument_dict.update(kwargs)
add_description(wrapper_id, argument_dict)
return case(**argument_dict)
case_wrappers.append(case_wrapper)
case_name = case.__name__ + str(wrapper_id)
case_wrapper.__name__ = case_name
if factory_args:
setattr(factory_args[0].__class__, case_name, case_wrapper)
return getattr(factory_args[0].__class__, case_name)
else:
return case_wrapper
return case_wrapper_wrapper()
return case_factory
class TestTest(object):
def __init__(self):
self.data = None
def setUp(self):
self.data = (1,2,3)
def test_all(self):
for index, value in enumerate((1,2,3)):
yield self.validate_equality(), index, value
#labelable_yielded_case
def validate_equality(self, index, value):
'''element {index} equals {value}'''
assert self.data[index] == value, 'expected %d got %d' % (value, self.data[index])

Categories

Resources