Passing multiple parameters to Setter method using assignment expression - python

I have a Question class with a setter method as follows.
#tokens.setter
def tokens(self, param: list, use_type: bool = False) -> None:
if not isinstance(param, list):
raise TypeError('Question.tokens must be a list')
if use_type:
self._tokens = None # assign something
else:
self._tokens = None # assign something else
After instantiating an object of class Question, I am trying to set its' tokens as follows.
question = Question(query['id'])
question.tokens = tokens, True # tokens is a list of words
I want to pass tokens as param and True as use_type parameter for the tokens setter method.
But I am getting a TypeError here as the first parameter param is getting a tuple as parameter.
How can I pass a value for the second parameter, use_type here? What is the correct syntax?

Separating values by comma without enclosing then in square brackets - [] - in Python, actually creates a tuple, not a list.
In fact, due to dynamic typing in Python, it is not considered a good practice to check exactly for a "list", when any sequence, or even iterable, will work equally well.
The second thing is that properties are meant to enable a single argument passing. Only the param parameter in your function will be used - the second parameter, use_type will not be passed by the property mechanism.
That means you have to read your arguments explicitly inside your method code, and a little extra logic is needed so that use_type is actually optional.
Actually, if you want to parametrize the settings of tokens with more than an argument, my advice would be to, in this case, just use a plain setter method, instead of resorting to properties or other descriptors. This will cause less surprise for people viewing (or having to write) code that use this class and set .tokens:
class Question:
...
def __init__(self, ...):
self.token = ... # Plain, unguarded attribute
..
def set_token(self, value, use_type = None):
...
But if you really want a property that will accept a sequence, with the second value being optional. However, if you are really using typing annotations and enforcing then, you have to restrict your params to tuples (which fortunately is the data type created by Python when you just separate objects by , as is in your code)
class Question:
...
#token.setter
def token(self, params: typing.Tuple[typing.Any, typing.Optional[bool]):
"""params should be a 1-or-2 tuple, with the second item, if present
a bool indicating "use_type"
"""
# The operation bellow will raise TypeError
# if params is not a tuple, so no need for the explicit check -
# just let the error propagate
value, use_type, *_ = params + (False,)
# if only one item is passed, "False" ends up in "use_type".
# With 2 itens, the extra "False" is assigned to "_" indicating
# it should be disregarded (it is a Python convention for '_' as a variable name)
if use_type:
...
And with this your question.tokens = tokens, True expression will work, as will
question.tokens = tokens,, but not question.tokens = tokens - as in this case, the value is not a tuple; (if it is a tuple it will be incorrectly used inside the method body as is) - I will say again that this is a strange pattern, and an explicit setter method should be preferred.

Related

Change parameter value for all methods in a class (decorator, metaclass, other option?)

I want to cleanup each parameter before passing it to the class methods. Right now I have smth like this:
from cerberus import Validator
class MyValidator(Validator): # Validator - external lib (has it's own _validate methods)
def _validate_type_name(self, value):
# validation code here
# goal - to clean up value before passing to each methods (mine + from lib) e.g., value.strip()
schema = {"name": {"type": "name"}, # name - custom type from _validate_type_name
"planet_type": {"type": "string"}} # string - external lib type
my_dict = {"name": " Mars ",
"planet_type": " terrestrial "}
v = MyValidator(schema)
print(v.validate(my_dict)) # True/ False
# NOTE: I would like to do cleanup on method type level (not pass to schema)
I would like to clean up data before passing to the MyValidator methods (e.g., simple strip) but I don't want to make it as a separate step (just in case someone forgets to execute it before calling validation). I'd like to integrate cleanup with validation methods (external ones + mine).
I was considering either decorator on class or metaclass, but maybe there's a better approach. I don't have much experience here, asking for your advice.
If your goal is to make sure that the caller does the cleaning (i.e. you want them to "clean" their own copy of the value rather than having you return a modified version to them, which necessitates that it happen outside your function), then a decorator can't do much more than enforcement -- i.e. you can wrap all the functions such that a runtime exception is raised if an invalid value comes through.
The way that I'd tackle this instead of a decorator would be with types (which requires that you include mypy in your testing process, but you should be doing that anyway IMO). Something like:
from typing import NewType
CleanString = NewType('CleanString', str)
clean(value: str) -> CleanString:
"""Does cleanup on a raw string to make it a 'clean' string"""
value = value.strip()
# whatever else
return CleanString(value)
class MyValidator(Validator):
def validate_name(self, value: CleanString) -> bool:
# this will now flag a mypy error if someone passes a plain str to it,
# saying a 'str' was provided where a 'CleanString' was required!
Static typing has the advantage of raising an error before the code is even executed, and regardless of the actual runtime value.

How to stack multiple calls? [duplicate]

I'm trying to create a function that chains results from multiple arguments.
def hi(string):
print(string)<p>
return hi
Calling hi("Hello")("World") works and becomes Hello \n World as expected.
the problem is when I want to append the result as a single string, but
return string + hi produces an error since hi is a function.
I've tried using __str__ and __repr__ to change how hi behaves when it has not input. But this only creates a different problem elsewhere.
hi("Hello")("World") = "Hello"("World") -> Naturally produces an error.
I understand why the program cannot solve it, but I cannot find a solution to it.
You're running into difficulty here because the result of each call to the function must itself be callable (so you can chain another function call), while at the same time also being a legitimate string (in case you don't chain another function call and just use the return value as-is).
Fortunately Python has you covered: any type can be made to be callable like a function by defining a __call__ method on it. Built-in types like str don't have such a method, but you can define a subclass of str that does.
class hi(str):
def __call__(self, string):
return hi(self + '\n' + string)
This isn't very pretty and is sorta fragile (i.e. you will end up with regular str objects when you do almost any operation with your special string, unless you override all methods of str to return hi instances instead) and so isn't considered very Pythonic.
In this particular case it wouldn't much matter if you end up with regular str instances when you start using the result, because at that point you're done chaining function calls, or should be in any sane world. However, this is often an issue in the general case where you're adding functionality to a built-in type via subclassing.
To a first approximation, the question in your title can be answered similarly:
class add(int): # could also subclass float
def __call__(self, value):
return add(self + value)
To really do add() right, though, you want to be able to return a callable subclass of the result type, whatever type it may be; it could be something besides int or float. Rather than trying to catalog these types and manually write the necessary subclasses, we can dynamically create them based on the result type. Here's a quick-and-dirty version:
class AddMixIn(object):
def __call__(self, value):
return add(self + value)
def add(value, _classes={}):
t = type(value)
if t not in _classes:
_classes[t] = type("add_" + t.__name__, (t, AddMixIn), {})
return _classes[t](value)
Happily, this implementation works fine for strings, since they can be concatenated using +.
Once you've started down this path, you'll probably want to do this for other operations too. It's a drag copying and pasting basically the same code for every operation, so let's write a function that writes the functions for you! Just specify a function that actually does the work, i.e., takes two values and does something to them, and it gives you back a function that does all the class munging for you. You can specify the operation with a lambda (anonymous function) or a predefined function, such as one from the operator module. Since it's a function that takes a function and returns a function (well, a callable object), it can also be used as a decorator!
def chainable(operation):
class CallMixIn(object):
def __call__(self, value):
return do(operation(self, value))
def do(value, _classes={}):
t = type(value)
if t not in _classes:
_classes[t] = type(t.__name__, (t, CallMixIn), {})
return _classes[t](value)
return do
add = chainable(lambda a, b: a + b)
# or...
import operator
add = chainable(operator.add)
# or as a decorator...
#chainable
def add(a, b): return a + b
In the end it's still not very pretty and is still sorta fragile and still wouldn't be considered very Pythonic.
If you're willing to use an additional (empty) call to signal the end of the chain, things get a lot simpler, because you just need to return functions until you're called with no argument:
def add(x):
return lambda y=None: x if y is None else add(x+y)
You call it like this:
add(3)(4)(5)() # 12
You are getting into some deep, Haskell-style, type-theoretical issues by having hi return a reference to itself. Instead, just accept multiple arguments and concatenate them in the function.
def hi(*args):
return "\n".join(args)
Some example usages:
print(hi("Hello", "World"))
print("Hello\n" + hi("World"))

Dynamically update all instances of multiple input function

I'm creating a program with a class that has 3 input attributes. The program calls a function that creates many of these objects with their inputs being given based on some other criteria not important to this question.
As I further develop my program, I may want to add more and more attributes to the class. This means that I have to go and find all instances of the function I am using to create these objects, and change the input arguments.
For example, my program may have many of these:
create_character(blue, pizza, running)
where inputs correspond to character's favorite color, food, and activity. Later, I may want to add a fourth input, such as favorite movie, or possibly a fifth or sixth or ninety-ninth input.
Do professional programmers have any advice for structuring their code so that they don't have to go through and individually change each line that the create_character function is called so that it now has the new, correct number of inputs?
Find and replace seems fine, but this makes error possible, and also seems tedious. I'm anticipating calling this function at least 50 times.
I can think of a few options for how you could design your class to make easier to extend later new kinds of "favorite" things.
The first approach is to make most (or all) of the arguments optional. That is, you should specify a default value for each one (which might be None if there's not a real value that could apply as a default). This way, when you add an extra argument, the existing places that call the function without the new argument will still work, they'll just get the default value.
Another option would be to use a container (like a dictionary) to hold the values, rather than using a separate variable or argument for each one. For instance, in your example could represent the character's favorites using a dictionary like favorites = {'color': blue, 'food': pizza, 'activity': running} (assuming the those values are defined somewhere), and then you could pass the dictionary around instead of the separate items. If you use the get method of the dictionary, you can also make this type of design use default values (favorites.get('movie') will return None if you haven't updated the code that creates the dictionary to add a 'movie' key yet).
You can take advantage of argument/keyword argument unpacking to support dynamically-changing function parameters. And also factory function/classes that generate the function you need:
def create_character(required1, required2, *opt_args, **kwargs):
""" create_character must always be called with required1 and required2
but can receive *opt_args sequence that stores arbitrary number of
positional args. kwargs hold a dict of optional keyword args """
for i, pos_arg in enumerate(opt_args):
# pos_arg walks opt_args sequence
print "position: {}, value: {}".format(i+3, pos_arg)
for keyword, value in kwargs:
print "Keyword was: {}, Value was: {}".format(keyword, value)
pos_args = (1,2,3)
create_character('this is required','this is also required', *pos_args)
""" position: 3, value: 1
position: 4, value: 2
position: 5, value: 3 """
a_dict = {
'custom_arg1': 'custom_value1',
'custom_arg2': 'custom_value2',
'custom_arg3': 'custom_value3'
}
create_character('this is required','this is also required', **a_dict)
""" Keyword was: custom_arg2, value: custom_value2
Keyword was: custom_arg3, value: custom_value3
Keyword was: custom_arg1, value: custom_value1 """
I really like the list or dictionary input method, but it was still messy and allowed for the possibility of error. What I ended up doing was this:
I changed the class object to have no inputs. Favorites were first assigned with random, default, or unspecified options.
After the class object was created, I then edited the attributes of the object, as so:
self.favorite_movie = "unspecified"
self.favorite_activity = "unspecified"
new_character = (character())
new_character.favorite_movie = "Dr. Strangelove"
I think that the downside to this approach is that it should be slower than inputting the variables directly. The upside is that this is easy to change in the future. Perhaps when the program is finished, it will make more sense to then convert to #Blckknight 's method, and give the input as a list or dictionary.

empty function object in python

I've heard that python functions are objects, similar to lists or dictionaries, etc. However, what would be a similar way of performing this type of action with a function?
# Assigning empty list to 'a'
a = list()
# Assigning empty function to 'a'
a = lambda: pass
# ???
How would you do this? Further, is it necessary or proper?
Here is the sense in which I would like to use it for better context:
I have a QListWidget for selecting items which are associated with keys in a dictionary. The values in this dictionary are also dictionaries, which hold certain properties of the items, which I can add. These certain properties are stored as keys, and the values in them are initialized or updated by calling different functions. So, I'm storing a variable in the window which gets updated when a button is pressed to tell this script which property to update.
As you can see, I would like to store the function to map to the data using the correct function based on the situation.
# Get selection from the list
name = selected_item
# Initialize an empty function
f = lambda: pass
# Use property that is being added now, which was updated by the specific button that was pushed
property_list = items[name][self.property_currently_being_added]
if self.property_currently_being_added == "prop1":
f = make_property1()
elif self.property_currently_being_added == "prop2":
f = make_property2()
elif self.property_currently_being_added == "prop3":
f = make_property3()
elif self.property_currently_being_added == "prop4":
f = make_property4()
# map the certain function to the data which was retrieved earlier
added_property = map(f, data)
property_list.append(added_property)
First, the reason this doesn't work:
a = lamdba: pass
… is that lambda only allows an expression, and defines a function that returns the value of the expression. Since pass is a statement, not an expression, this is illegal.
However, this works just fine:
a = lambda: None
In Python, a function that falls off the end without a return statement always returns None. So, these are equivalent:
def a(): return None
def a(): pass
However, I don't see why you want to write this as a lambda and an assignment anyway; the def is shorter, and more readable, and gives you an introspectable function object with a nice name (a instead of <lambda>), and so on. The only reasons to ever use lambda are when you don't want to give the function a name, or when you need to define the function inside an expression. Obviously neither of those are true, because you use the lambda directly inside an assignment statement. So, just use def.
Meanwhile, this is in a sense an "empty function", or at least as empty as possible (as you can see by, e.g., calling dis.dis(a), it still takes two bytecodes to do nothing but fall off the end and return None), but it's not useful for your case. You don't want an "empty function". If you try passing your a to map, you're just going to get a TypeError, because you're trying to call a function of no arguments with one argument. (Because that's what map does.)
What you might want is an identity function, which just returns its argument as-is. Like this:
def a(x): return x
But I'm not sure that's what you want. Did you want to append data as-is in that case? Or did you want to do something different, like return early, or raise an exception, or not append anything, or …?
Finally, I don't see why you want a function at all. Why not just not call map if you have nothing to map? You have a perfectly good else clause that already catches that case (especially handy if what you want to do is return early or raise…). Or, if you prefer, you can start with f = None, and then use an if f: do decide whether to map or not. Or, if you really want:
added_property = [f(element) if f else element for element in data]
… or …
added_property = map(f, data) if f else data
As one last note, instead of a long if/elif chain that repeats the same thing over and over again, you might want a dict:
propfuncs = {'prop1': make_property1(),
'prop2': make_property2(),
'prop3': make_property3(),
'prop4': make_property4()}
Then, all that cruft turns into these two lines:
f = propfuncs.get(self.property_currently_being_added)
added_property = map(f, data) if f else data
Or course an even better design might be to replace all those make_propertyN functions with a single function that you call as make_property(1) or make_property('prop1')… but without seeing what they actually do, I can't be sure of that.
For completeness and since the title is "empty function object in python", more general case is an empty function object that takes any number of parameters, so you can use it in any callback. It's this one:
callback = lambda *_, **__: None
Explanation is here: http://echochamber.me/viewtopic.php?t=64825
I am surprised to learn that you can even do...
def a(): "This is a test"
a()
this feels so much like you're looking for a Nothing functor, I am guessing that if you had knowledge of Monads you wouldn't even need an empty function , as inspiration PyMonad has a nice Nothing implementation, I usually like to create my own, but it's a good starting point.

Duck typing to allow tuples, lists, or something that can be treated like one

def foo(spam, obj_of_interest):
"""Pass a _____ and an object of interest, and return [something that does something worthwhile] """
name = spam[0]
quest = spam[1]
fav_color = spam[2]
# ... interesting code
return obj_of_interest
You'll note that foo() can function perfectly regardless of whether it's passed spam as a list, as a tuple, or really as anything that enforces an order to the element and can be addressed like a list.
How do you document this fact without telling the user to use a specific type?
You say,
"""spam is an object that supports indexing."""

Categories

Resources