Why is foo(*arg, x) not allowed in Python? - python

Look at the following example
point = (1, 2)
size = (2, 3)
color = 'red'
class Rect(object):
def __init__(self, x, y, width, height, color):
pass
It would be very tempting to call:
Rect(*point, *size, color)
Possible workarounds would be:
Rect(point[0], point[1], size[0], size[1], color)
Rect(*(point + size), color=color)
Rect(*(point + size + (color,)))
But why is Rect(*point, *size, color) not allowed, is there any semantic ambiguity or general disadvantage you could think of?
EDIT: Specific Questions
Why are multiple *arg expansions not allowed in function calls?
Why are positional arguments not allowed after *arg expansions?

I'm not going to speak to why multiple tuple unpacking isn't part of Python, but I will point out that you're not matching your class to your data in your example.
You have the following code:
point = (1, 2)
size = (2, 3)
color = 'red'
class Rect(object):
def __init__(self, x, y, width, height, color):
self.x = x
self.y = y
self.width = width
self.height = height
self.color = color
but a better way to express your Rect object would be as follows:
class Rect:
def __init__(self, point, size, color):
self.point = point
self.size = size
self.color = color
r = Rect(point, size, color)
In general, if your data is in tuples, have your constructor take tuples. If your data is in a dict, have your constructor take a dict. If your data is an object, have your constructor take an object, etc.
In general, you want to work with the idioms of the language, rather than try to work around them.
EDIT
Seeing how popular this question is, I'll give you an decorator that allows you to call the constructor however you like.
class Pack(object):
def __init__(self, *template):
self.template = template
def __call__(self, f):
def pack(*args):
args = list(args)
for i, tup in enumerate(self.template):
if type(tup) != tuple:
continue
for j, typ in enumerate(tup):
if type(args[i+j]) != typ:
break
else:
args[i:i+j+1] = [tuple(args[i:i+j+1])]
f(*args)
return pack
class Rect:
#Pack(object, (int, int), (int, int), str)
def __init__(self, point, size, color):
self.point = point
self.size = size
self.color = color
Now you can initialize your object any way you like.
r1 = Rect(point, size, color)
r2 = Rect((1,2), size, color)
r3 = Rect(1, 2, size, color)
r4 = Rect((1, 2), 2, 3, color)
r5 = Rect(1, 2, 2, 3, color)
While I wouldn't recommend using this in practice (it violates the principle that you should have only one way to do it), it does serve to demonstrate that there's usually a way to do anything in Python.

As far as I know, it was a design choice, but there seems to be a logic behind it.
EDIT: the *args notation in a function call was designed so you could pass in a tuple of variables of an arbitrary length that could change between calls. In that case, having something like f(*a, *b, c) doesn't make sense as a call, as if a changes length all the elements of b get assigned to the wrong variables, and c isn't in the right place either.
Keeping the language simple, powerful, and standardized is a good thing. Keeping it in sync with what actually goes on in processing the arguments is also a very good thing.
Think about how the language unpacks your function call. If multiple *arg are allowed in any order like Rect(*point, *size, color), note that all that matters to properly unpack is that point and size have a total of four elements. So point=(), size=(1,2,2,3), andcolor='red') would allow Rect(*point, *size, color) to work as a proper call. Basically, the language when it parses the *point and *size is treating it as one combined *arg tuple, so Rect(*(point + size), color=color) is more faithful representation.
There never needs to be two tuples of arguments passed in the form *args, you can always represent it as one. Since assignment of parameters is only dependent on the order in this combined *arg list, it makes sense to define it as such.
If you can make function calls like f(*a, *b), the language almost begs to allow you to define functions with multiple *args in the parameter list, and those couldn't be processed. E.g.,
def f(*a, *b):
return (sum(a), 2*sum(b))
How would f(1,2,3,4) be processed?
I think this is why for syntactical concreteness, the language forces function calls and definitions to be in the following specific form; like f(a,b,x=1,y=2,*args,**kwargs) which is order dependent.
Everything there has a specific meaning in a function definition and function call. a and b are parameters defined without default values, next x and y are parameters defined with default values (that could be skipped; so come after the no default parameters). Next, *args is populated as a tuple with all the args filled with the rest of the parameters from a function call that weren't keyword parameters. This comes after the others, as this could change length, and you don't want something that could change length between calls to affect assignment of variables. At the end **kwargs takes all the keyword arguments that weren't defined elsewhere. With these concrete definitions you never need to have multiple *args or **kwargs.

*point says that you are passing in a whole sequence of items - something like all the elements in a list, but not as a list.
In this case, you cannot limit how many elements are being passed in. Therefore, there is no way for the interpreter to know which elements of the sequence are part of *points and which are of *size
For example, if you passed the following as input: 2, 5, 3, 4, 17, 87, 4, 0, can you tell me, which of those numbers are represented by *points and which by *size? This is the same problem that the interpreter would face as well
Hope this helps

Python is full of these subtle glitches. For example you can do:
first, second, last = (1, 2, 3)
And you can't do:
first, *others = (1, 2, 3)
But in Python 3 you now can.
Your suggestion probably is going to be suggested in a PEP and integrated or rejected one day.

Well, in Python 2, you can say:
point = 1, 2
size = 2, 3
color = 'red'
class Rect(object):
def __init__(self, (x, y), (width, height), color):
pass
Then you can say:
a_rect= Rect(point, size, color)
taking care that the first two arguments are sequences of len == 2.
NB: This capability has been removed from Python 3.

Related

Most pythonic way to provide defaults for class constructor

I am trying to stick to Google's styleguide to strive for consistency from the beginning.
I am currently creating a module and within this module I have a class. I want to provide some sensible default values for different standard use cases. However, I want to give the user the flexibility to override any of the defaults. What I am currently doing is I provide a module scoped "constant" dictionary with the default values (for the different use cases) and in my class I give the parameters in the constructor precedence over the defaults.
Finally, I want to make sure that we end with valid values for the parameters.
That's what I have done:
MY_DEFAULTS = {"use_case_1": {"x": 1, "y": 2},
"use_case_2": {"x": 4, "y": 3}}
class MyClass:
def __init__(self, use_case = None, x = None, y = None):
self.x = x
self.y = y
if use_case:
if not self.x:
self.x = MY_DEFAULTS[use_case]["x"]
if not self.y:
self.y = MY_DEFAULTS[use_case]["y"]
assert self.x, "no valid values for 'x' provided"
assert self.y, "no valid values for 'y' provided"
def __str__(self):
return "(%s, %s)" % (self.x, self.y)
print(MyClass()) # AssertionError: no valid values for 'x' provided
print(MyClass("use_case_1")) # (1, 2)
print(MyClass("use_case_2", y = 10) # (4, 10)
Questions
While technically working, I was wondering whether this is the most pythonic way of doing it?
With more and more default values for my class the code becomes very repetitive, what could I do to simplify that?
assert seems also for me not the best option at it is rather a debugging statement than a validation check. I was toying with the #property decorator, where I would raise an Exception in case there are invalid parameters, but with the current pattern I want to allow x and y for a short moment to be not truthy to implement the precedence properly (that is I only want to check the truthiness at the end of the constructor. Any hints on that?
In general if there is more than one way to reasonably construct your object type, you can provide classmethods for alternate construction (dict.fromkeys is an excellent example of this). Note that this approach is more applicable if your use cases are finite and well defined statically.
class MyClass:
def __init__(self, x, y):
self.x = x
self.y = y
#classmethod
def make_use_case1(cls, x=1, y=2):
return cls(x,y)
#classmethod
def make_use_case2(cls, x=4, y=3):
return cls(x,y)
def __str__(self):
return "(%s, %s)" % (self.x, self.y)
If the only variation in the use cases is default arguments then re-writing the list of positional arguments each time is a lot of overhead. Instead we can write one classmethod to take the use case and the optional overrides as keyword only.
class MyClass:
DEFAULTS_PER_USE_CASE = {
"use_case_1": {"x": 1, "y": 2},
"use_case_2": {"x": 4, "y": 3}
}
#classmethod
def make_from_use_case(cls, usecase, **overrides):
args = {**cls.DEFAULTS_PER_USE_CASE[usecase], **overrides}
return cls(**args)
def __init__(self, x,y):
self.x = x
self.y = y
def __str__(self):
return "(%s, %s)" % (self.x, self.y)
x = MyClass.make_from_use_case("use_case_1", x=5)
print(x)
If you wanted the arguments to be passed positionally that would be more difficult but I imagine this would suit your needs.
Python is a very flexible language. If your code runs, there is no technically wrong way of doing things. However, if you want to be "Pythonic", here are a few tips for you. First of all, you should never use AssertionErrors for verifying the presence or value of a parameter. If a parameter is not passed and it should be there, you should raise a TypeError. If the value passed is not acceptable, you should raise a ValueError. Assertions are mainly used for testing.
When you want to verify the presence of a value in the parameter a, it is best to do a is not None, rather than not a. You can do not a when None and 0 or other Falsy values are equally invalid for you. However, when the purpose is to check the presence of a value, 0 and None are not the same.
Regarding your class, I believe that a nicer way of doing this is unwrapping the values of the dictionary upon the class initalization. If you remove use_case from the function signature, and call your class like this:
MyClass(**MY_DEFAULTS["use_case_1"])
Python will unwrap the values of the nested dictionary and pass them as keyword arguments to your __init__ method. If you do not want the values to be optional, remove the default value and Python will raise a TypeError for you if the parameters provided do not match the function signature.
If you still want your parameters to not be Falsy, perhaps you should want to provide a more concrete scope for the possible values of the parameters. If the type of x is int, and you don't want 0 values, then you should compare x with 0:
def __init__(x, y):
if x == 0 or y == 0:
raise ValueError("x or y cannot be 0")
keeping your original interface, you could use kwargs to read parameters. If some are missing, set the defaults, only if the use case matches.
MY_DEFAULTS = {"use_case_1": {"x": 1, "y": 2},
"use_case_2": {"x": 4, "y": 3}}
class MyClass:
def __init__(self, use_case = None, **kwargs):
for k,v in kwargs.items():
setattr(self,k,v)
if use_case:
for k,v in MY_DEFAULTS[use_case].items():
if k not in kwargs:
setattr(self,k,v)
unassigned = {'x','y'}
unassigned.difference_update(self.__dict__)
if unassigned:
raise TypeError("missing params: {}".format(unassigned))
def __str__(self):
return "(%s, %s)" % (self.x, self.y)
print(MyClass("use_case_1")) # (1, 2)
print(MyClass("use_case_2", y = 10)) # (4, 10)
print(MyClass())
executing this:
(1, 2)
(4, 10)
Traceback (most recent call last):
File "<string>", line 566, in run_nodebug
File "C:\Users\T0024260\Documents\module1.py", line 22, in <module>
print(MyClass())
File "C:\Users\T0024260\Documents\module1.py", line 15, in __init__
raise TypeError("missing params: {}".format(unassigned))
TypeError: missing params: {'y', 'x'}
With more and more default values for my class the code becomes very repetitive, what could I do to simplify that?
This solution allows to have many parameters.

How to control type casting for my classes

How can I control type casting for my classes? I know there is __int__() or __str__(), but what about other types? E.g. I have two classes Point and Vector. Is it possible to do something like:
point = Point(1, 2, 3)
# the following should not call Vector._init_(), but use a cast defined in Point
# (something like Point.__Vector__())
vector = Vector(point)
Is this possible? If so, how can I achieve such behavior? If not, for what standard types could I define a cast function to allow e.g. tuple(point) or list(point) - couldn't find this in the python documentation.
You could do it via the use of classmethods.
For example,
from point import Point # where the Point object is defined
class Vector:
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
#classmethod
def from_point(cls, point):
if not isinstance(point, Point): # some validation first
raise TypeError("The given object is not of type 'Point'")
return cls(point.x, point.y, point.z)
# other methods
Thus, then if you have a p = Point(1, 0, 0) then you can do v = Vector.from_point(p) and you'll have what you were looking for.
There are quite some improvements that could be applied but depend on the details of Point.

Passing function returns as method parameters in Python

I'm currently writing a small code to move two balls around in a Tkinter GUI and do some other stuff too.
I've already written a code that works, but as it uses a lot of global variables I tried to improve it.
Below I've pasted the code section relative to my question :
can.coords needs five parameters : the object you wanna 'move', and the new coordinates.
The returns from both moveLeft() and addThirty() are two items lists.
Of course, the star operator (to unpack a list) doesn't work .
How do I pass the four items from the two returned lists of the functions into the method .coords() ?
PS : I'm new to Python and even to programming.
def moveo (lr, tb):
global newX, newY
newX = x+lr
newY = y+tb
return newX, newY
def moveLeft ():
coordins = moveo (-10, 0)
return coordins
def addThirty (func):
i = 0
coordinsNew = func
coordinsNew = list(coordinsNew)
while i < 2:
coordinsNew[i] = coordinsNew[i]+30
i += 1
return coordinsNew
Button(wind, text = 'Left', command=can.coords (oval1,(*moveLeft()),(*addThirty(moveLeft()))))
You can always combine two lists or two tuples into one just with +:
can.coords(oval1, *(moveLeft() + addThirty(moveLeft())))
Even if you've got sequences (or even iterators) of different types, you can always convert them:
can.coords(oval1, *(moveLeft() + tuple(addThirty(moveLeft())))))
However, you really should step back and ask why this needs to be one line in the first place. It scrolls off the right edge of the screen, it requires enough complex parenthesization that you have to think about it to understand it, etc. Why not just do this:
top, left = moveLeft()
bottom, right = addThirty(moveLeft())
can.coords(oval1, top, left, bottom, right)
In a comment, you say:
I can't do this because I want the coordinates to change every time I press the button. So the button needs to : execute both functions to modify the coordinates and pass them to can.coords () in one time.
Just putting it in one line doesn't do that, or even help make that easier. The way you've written it, you're calling can.coords once, and passing the resulting return value as the command. That's not what you want. What you need to pass is a function that does all this stuff.
Which means that you definitely want to split it up into multiple lines. For example:
def update_coords():
top, left = moveLeft()
bottom, right = addThirty(moveLeft())
can.coords(oval1, top, left, bottom, right)
Button(wind, text = 'Left', command=update_coords)
Because the only way to put it in one line would be with an equivalent lambda or partial, which will be even more unreadable than a call; something like:
Button(wind, text = 'Left', command=lambda: can.coords(oval1, *(moveLeft() + addThirty(moveLeft()))))
To explain the difference between passing a function, and calling a function and passing its return value, let's take a much simpler example:
>>> def foo():
... return 2
>>> print(foo)
<function foo at 0x12345678>
>>> print(foo())
2
Here, it should be pretty clear what the difference is. foo is an expression whose value is the function foo itself. But foo() is an expression whose value is determined by calling foo with no arguments, then using whatever was returned (in this case, 2).
If we make it a little more complicated, it's no different:
>>> def bar(x, y):
... return x+y
>>> print(bar)
<function bar at 0x12345680>
>>> print(bar(2, 3))
6
So, it's obvious how you can pass around bar itself, or how you can pass the 6 you get back from bar(2, 3)… but what if you want to pass a function that can be called with no arguments and return the same thing that bar(2, 3) would return? Well, you don't have such a thing; you have to create it.
You can do this in two ways: creating a new function:
>>> def new_function():
... return bar(2, 3)
… or partially evaluating the function:
>>> new_function = partial(bar, 2, 3)
Your case adds a few extra wrinkles: you're starting with a bound method rather than a function, you need to make sure the arguments get evaluated each time the new function is run (because calling moveLeft() twice each time rather than just once is every bit as important as calling can.coords each time), and you've got a bunch of arguments that you get in a complicated way. But none of those wrinkles make things any harder; you just have to look past them:
>>> def new_function():
... can.coords(oval1, *(moveLeft() + addThirty(moveLeft())))
(The partial would be a lot harder to write, because you have to compose a sequence of functions together just to get the parameters, which you need to partial as well… but whenever partial isn't trivial in Python, don't try to figure it out, just write an explicit function.)
If both functions return the same type (list or tuple) then just do:
can.coords(oval1, *(moveLeft() + addThirty(moveLeft())))
If they return different types (tuple, list, iterator, whatever) do:
args = list(moveLevt())
args.extend(addThirty(moveLeft()))
can.coords(oval1, *args)
Sorry for digging up this topic, but after an interesting answer from a user on another topic, I thought I'll improve the answer to this question. Actually, you can assign a function with arguments to a command as long as it returns a function. In this case, it will avoid you a lot of trouble as you don't need to write a new function for every left right, down etc.
As you see, I can use arguments to the functions I assign to command:
command=move1(0,10)
I've written the code for only one oval, just to show how it works.
from tkinter import *
x1, y1 = 135, 135
x2, y2 = 170, 170
def move1 (x, y):
def moveo1 ():
global x1, y1
x1, y1 = x1+x, y1+y
can.coords (oval1, x1, y1, x1+30, y1+30)
return moveo1
##########MAIN############
wind = Tk()
wind.title ("Move Da Ball")
can = Canvas (wind, width = 300, height = 300, bg = "light blue")
can.pack (side = LEFT,padx = 5, pady = 5)
oval1 = can.create_oval(x1,y1,x1+30,y1+30,width=2,fill='orange') #Planet 1
Button(wind, text = 'Left', command=move1(-10,0)).pack(padx = 5, pady = 5)
Button(wind, text = 'Right', command=move1(10,0)).pack(padx = 5, pady = 5)
Button(wind, text = 'Top', command=move1(0,-10)).pack(padx = 5, pady = 5)
Button(wind, text = 'Bottom', command=move1(0,10)).pack(padx = 5, pady = 5)
Button(wind, text = 'Quit', command=wind.destroy).pack(padx = 5, pady = 5)
wind.mainloop()

In Python, how can I make a function accept a sequence of variables embedded in a string?

In Python:
a = "l[0], l[1], l[2], l[3]"
# l[0] etc. have their own value
my_function(a) #doesn't work
#but this does work
my_function(l[0], l[1], l[2], l[3])
Can the string a be 'transformed' so that the function sees the variables?
Thank you.
Later update: Thank you for answering my question.
I am including the bigger problem and why I had to resort to the eval function (not desired according to your answers).
The function:
class Line(object):
def __init__(self, pt1, pt2, *pt):
pt1 = Point(pt1[0],pt1[1])
pt2 = Point(pt2[0],pt2[1])
self.vertices = [pt1, pt2]
for i in pt:
self.vertices.append(Point(i[0],i[1]))
def __getitem__(self, key):
pt = self.vertices[key]
p = Point(p[0],p[1])
return p
#Here is the part with the issue:
def move(self, dx, dy):
pts = len(self.vertices)
l = self.vertices
pt1 = Point(Point(l[0].x, l[0].y).move(dx, dy).x, Point(l[0].x, l[0].y).move(dx, dy).y)
pt2 = Point(Point(l[1].x, l[1].y).move(dx, dy).x, Point(l[1].x, l[1].y).move(dx, dy).y)
if pts == 2:
mv = LineString(p1, p2)
if pts > 2:
bla = ''
for i in [2,pts]:
px = Point(l[i].x, l[i].y).move(dx, dy).x
py = Point(l[i].x, l[i].y).move(dx, dy).y
l[i] = Point(px,py)
bla += 'l[' + str(i) + '], '
arguments = bla[:-2]
mv = LineString(pt1, pt2, *eval(arguments))
return mv
According to your answers, there are better ways of solving this..
To do this, you can combine the eval function to evaluate the string with the * operator to apply my_function on the resulting tuple:
my_function(*eval(a))
However, doing this without a very good reason is almost always an indication of bad design. eval makes your code vulnerable to run-time errors and code injection attacks, and removes important performance optimizations. If you describe your actual requirements, it is likely that someone can propose a better solution.
You had an XY problem.
Your real X problem was to pass an arbitrary number of objects to a function.
After having defined a string containing the identifiers of these objects, the Y solution to extract back the objects from this string became your graal.
But I'm pretty sure that the following snippet answers to your real X problem:
if pts > 2:
blal = []
for i in [2,pts]:
px = Point(l[i].x, l[i].y).move(dx, dy).x
py = Point(l[i].x, l[i].y).move(dx, dy).y
blal.append(Point(px,py))
mv = LineString(pt1, pt2, *blal)
It's a facility offered by Python to pass arbitrary number of arguments to a function, not present in all programming languages. Would be a pity to complicate one's life , not using it.
I think that new members should be informed, or even prevented, not to upvote and accept answers too rapidly.
EDIT
I think that you can replace the code block in your question with this one:
from itertools import chain
class Line(object):
def __init__(self, pt1, pt2, *pt):
self.vertices = map(lambda x: Point(*x),
chain((pt1,pt2), pt))
def __getitem__(self, key):
return Point(*self.vertices[key])
def move(self, dx, dy):
return LineString(*[ Point(Point(v.x, v.y).move(dx, dy).x,
Point(v.x, v.y).move(dx, dy).y)
for v in self.vertices])
You could even define Point() in such a way that it would accept a couple (tuple, list... , I dont't know what your pts are) instead of elements of pt
So, you could write Point(x) instead of Point(*x) and
self.vertices = map(Point, chain((pt1,pt2), pt))
instead of
self.vertices = map(lambda x: Point(*x),
chain((pt1,pt2), pt))
but would need to write Point((v.x,v.y)) instead of Point(v.x,v.y)
Python methods take arbitrary argument lists. What this means is that your function can take any number of arguments.
Here is an example:
def foo(*args, **kwargs):
print args
foo('a','b','c','d')
Now, suppose you wanted to pass a list with values in it as arguments:
mylist = ['a','b','c','d']
foo(mylist)
Now this will result in:
(['a', 'b', 'c', 'd'],)
A tuple with your list as the first argument. What we want is the same affect as foo('a','b','c','d'). To get that, we need to expand the list, like this:
foo(*mylist)
Now you'll get the same result:
('a', 'b', 'c', 'd')
Taking this and applying it to your problem:
def foo(*args, **kwargs):
print "Total arguments: {}".format(len(args))
v = "a, b, c"
>>> foo(v)
Total arguments: 1
>>> foo(*v.split(','))
Total arguments: 3

How to initialize an instance of a subclass of tuple in Python? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Subclassing Python tuple with multiple __init__ arguments
I want to define a class which inherits from tuple, and I want to be able to instantiate it using a syntax not supported by tuple. For a simple example, let's say I want to define a class MyTuple which inherits from tuple, and which I can instantiate by passing two values, x and y, to create the (my) tuple (x, y). I've tried the following code:
class MyTuple(tuple):
def __init__(self, x, y):
print("debug message")
super().__init__((x, y))
But when I tried, for example, MyTuple(2, 3) I got an error: TypeError: tuple() takes at most 1 argument (2 given). It seems my __init__ function was not even called (based on the error I got and on the fact my "debug message" was not printed).
So what's the right way to do this?
I'm using Python 3.2.
class MyTuple(tuple):
def __new__(cls, x, y):
return tuple.__new__(cls, (x, y))
x = MyTuple(2,3)
print(x)
# (2, 3)
One of the difficulties of using super is that you do not control which classes's method of the same name is going to be called next. So all the classes' methods have to share the same call signature -- at least the same number of items. Since you are changing the number of arguments sent to __new__, you can not use super.
Or as Lattyware suggests, you could define a namedtuple,
import collections
MyTuple = collections.namedtuple('MyTuple', 'x y')
p = MyTuple(2,3)
print(p)
# MyTuple(x=2, y=3)
print(p.x)
# 2
another approach would be to encapsulate a tuple rather than inheriting from it:
>>> class MyTuple(object):
count = lambda self, *args: self._tuple.count(*args)
index = lambda self, *args: self._tuple.index(*args)
__repr__ = lambda self: self._tuple.__repr__()
# wrap other methods you need, or define them yourself,
# or simply forward all unknown method lookups to _tuple
def __init__(self, x, y):
self._tuple = x,y
>>> x = MyTuple(2,3)
>>> x
(2, 3)
>>> x.index(3)
1
How practical this is, depends on how many capabilities and modifications you need, and wheter you need to have isinstance(MyTuple(2, 3), tuple).

Categories