Using keyword arguments in __getitem__ method in Python - python

I want to define a class Foo whose objects can be used like, foo[1, a=2].
I tried to achieve this by decorating the __getitem__ method of
Foo but with no success. Below is the example code.
def decorator(func):
def func_(*args, **kewargs):
if 'a' in kewargs:
args = list(args) + [kewargs['a']]
return func(*args)
else:
return func(*args)
return func_
class Foo(object):
#decorator
def __getitem__(self, *items):
return items
foo = Foo()
>>> foo.__getitem__(2, a=10)
(2, 10)
>>> foo[2, a=10]
SyntaxError: invalid syntax
So foo[...] is not equivalent to foo.__getitem__(...), something
behind the scene is done for the former. My question is what exactly and how
can I make foo[2, a=10] to work, if at all.

Python allows implicit tuple creation (without parentheses):
In [2]: tup = 1, 2, 3
In [3]: tup
Out[3]: (1, 2, 3)
And it works the same inside square brackets:
In [4]: d = {(1, 2, 3): 4}
In [5]: d[1, 2, 3]
Out[5]: 4
But (2, a=10) is not a valid tuple literal:
In [6]: (2, a=10)
File "<ipython-input-1-7dc03602f595>", line 1
(2, a=10)
^
SyntaxError: invalid syntax
Simply put, you can't make foo[2, a=10] to work, because it's a syntax error no matter how you tweak your __getitem__ implementation.
I'd probably define an ordinary method e.g. get and use it like Foo.get(2, a=10).

This is proposed for python 3.6
Using keyword arguments for indexing is currently a syntax error.
However, PEP472 (https://www.python.org/dev/peps/pep-0472/) proposes this addition to the python syntax.
Workarounds
The PEP also shows workarounds that are currently valid:
foo[2, "a":10] or foo[2, {"a":10}]

Related

Unfamiliar Python syntax (e.g. obj1 = <class method>(<method parameters>)(obj2))

I'm taking an AI / ML online course, and some of the assignments include Python language phrases like:
input_img = tf.keras.Input(shape=input_shape)
Z1 = tfl.Conv2D(8, kernel_size=4,strides=(1,1), padding='SAME')(input_img)
It seems that the general syntax of the second line is:
obj1 = <class method>(<method parameters>)(obj2),
where obj1, obj2 are some class instances.
Could not find an explanation to this syntax.
Please direct me to a reference / example clarifying said syntax.
The syntax is that tfl.Conv2D(8, kernel_size=4,strides=(1,1), padding='SAME') returns something that is callable, like a callable object OR a method, then you call it with input_img params
A simple example
class A:
def __init__(self, *args):
self.values = list(args)
def run(self, *more_values):
print(f"A.run with {self.values} - {more_values}")
class B:
#staticmethod
def get_method(a, b, c):
return A(a, b, c).run
In the case B.get_method() returns a run method that is not called, so when retrieving it, you can call it, and split in 2 steps to understand better
B.get_method(1, 2, 3)(4, 5) # A.run with [1, 2, 3] - (4, 5)
my_method = B.get_method(1, 2, 3)
my_method(4, 5) # A.run with [1, 2, 3] - (4, 5)

How to make a function L with lambda that return true if if x is in the set L

I'm trying to write a function using lambda and assign it to a variable, L for example. It should return true if passed argument x is contained in the set L:
L = { (1, 2, 3), (4, 5, 6)} # example
x = (2, 3, 5) #example
L = lambda x : list.__contains__(L, x)
doesn't seem to work.
The idea is i want to use L as a variable and as a function as the same time.
Where I'm wrong? What is the best way to achieve this?
Use in:
func = lambda elem, cont: elem in cont
li = [1, 2, 3]
print(func(1, li))
>> True
print(func(4, li))
>> False
But for this kind of usage a normal def syntax can be used for sake of simplicity.
From PEP-8:
"Always use a def statement instead of an assignment statement that binds a lambda expression directly to an identifier."
Why do not use:
def check(obj, iterable):
return True if obj in iterable else False
?
When you define a lambda function, it will assigned to L variable without being executed, so the original content of L will be lost before using it and will refer a function but not to a set.
If you will try:
L = { (1, 2, 3), (4, 5, 6)}
L = lambda x: x in L
L((1, 2, 3))
you will get an error:
TypeError: argument of type 'function' is not iterable
As you can see, at the moment when using L, it will be of type function and not a set anymore. So this is a wrong approach, unless you are thinking to use more that one context.
To do it in the right way, you should have one variable which links to your data set and another for your function (lambda).
L = {(1, 2, 3), (4, 5, 6)}
F = lambda x: x in L
if F((1, 2, 3)):
print("was found in L")

Is there such thing as "apply" in python?

I wonder, is there a function in python -let's call it now apply- that does the following:
apply(f_1, 1) = f_1(1)
apply(f_2, (1, 2)) = f_1(1, 2)
...
apply(f_n, (1, 2,..., n)) = f_n(1, 2,..., n) # works with a tuple of proper length
Since it does exist in eg. A+ and Mathematica and it used to be really useful for me.
Cheers!
Python has language-level features for this, known as "argument unpacking", or just "splat".
# With positional arguments
args = (1, 2, 3)
f_1(*args)
# With keyword arguments
kwargs = {'first': 1, 'second': 2}
f_2(**kwargs)
You can use the * operator for the same effect:
f_1(*(1, 2)) = f_1(1, 2)
...
The expression following the * needn't be a tuple, it can be any expression that evaluates to a sequence.
Python also has a built-in apply function that does what you'd expect, but it's been obsolete in favor of the * operator since Python 2.3. If you need apply for some reason and want to avoid the taint of deprecation, it is trivial to implement one:
def my_apply(f, args):
return f(*args)
Yep, use the * operator on the list of arguments. For a practical example:
max(1, 2, 3, 4, 5) # normal invocation
=> 5
max(*[1, 2, 3, 4, 5]) # apply-like invocation
=> 5
Think of the second snippet as equivalent to apply(max, [1, 2, 3, 4, 5])

How to convert list to tuple and search for max and min

I am currently learning python coding and I come across this qns on a learning site:
Create a function that takes a sequence of inputs (may be a list, a tuple, or just a bunch of inputs). The function should return the minimum and the maximum of the list.
This are some of the test values that are using:
minmax(5,4)
4,5
minmax(5,4,8,3,5,7,4)
3,8
minmax([5,4,6])
4,6
minmax(5.1,4.2,68.34,129.1,-90.3)
-90.3,129.1
And I had tried doing it this way but when the parameter is a list, I can't seems to convert it into a tuple and find the max and min.
Here is what I had tried:
def minmax(*a):
b = tuple(a)
minNum = min(b)
maxNum = max(b)
c = (minNum, maxNum)
return c
When a list is taken in, the return result is ([5, 4, 6], [5, 4, 6])
def minmax(*a):
if len(a) == 1: # Single (list, tuple, or scalar) argument
try:
return minmax(*a[0]) # Expansion of sequence elements, if possible
except TypeError: # Case of minmax(42)
pass # The general code below handles this directly
return (min(a), max(a))
>>> minmax(3, 5, 1, 10)
(1, 10)
>>> minmax([3, 5, 1, 10])
(1, 10)
>>> minmax((42, 123, -12))
(-12, 123)
>>> minmax(42)
42
This works in more cases than the built-in min() and max(), which do not work on a single scalar argument (min(42)).
>>> min(42)
TypeError: 'int' object is not iterable
It is however possible to write a simpler version that behaves like the built-in min() and max() (see my other answer, for instance).
This works by forcing min() to be given strictly more than 1 element, except in the special case of minmax(42), which calls min((42,)).
To be able to handle different ways of passing in arguments, you need to have some conditions to handle each case.
>>> def minmax(*a):
... if len(a) == 1 and hasattr(a[0], '__getitem__'):
... # handle a single sequence passed in
... return min(a[0]), max(a[0])
... # handle values passed in
... return min(a), max(a)
...
>>> minmax(5, 4)
(4, 5)
>>> minmax(5, 4, 8, 3, 5, 7, 4)
(3, 8)
>>> minmax([5, 4, 6])
(4, 6)
>>> minmax(5.1, 4.2, 68.34, 129.1, -90.3)
(-90.3, 129.1)
A simpler but slightly less powerful solution that mirrors my other solution is:
def minmax(*a):
if len(a) == 1: # Single (list or tuple) argument
return (min(a[0]), max(a[0]))
return minmax(a) # Single tuple argument given to minmax()
This version forces min() to be given a single (list or tuple) argument.
It behaves like the built-in min() and max(): min(42) and minmax(42) both raise an exception:
>>> minmax(3, 5, 1, 10)
(1, 10)
>>> minmax([3, 5, 1, 10])
(1, 10)
>>> minmax((42, 123, -12))
(-12, 123)
>>> minmax(42)
TypeError: 'int' object is not iterable
>>> min(42)
TypeError: 'int' object is not iterable

Why does unpacking a tuple cause a syntax error?

In Python, I wrote this:
bvar=mht.get_value()
temp=self.treemodel.insert(iter,0,(mht,False,*bvar))
I'm trying to expand bvar to the function call as arguments.
But then it returns:
File "./unobsoluttreemodel.py", line 65
temp=self.treemodel.insert(iter,0,(mht,False,*bvar))
^
SyntaxError: invalid syntax
What just happen? It should be correct right?
Update: this behavior was fixed in Python 3.5.0, see PEP-0448:
Unpacking is proposed to be allowed inside tuple, list, set, and dictionary displays:
*range(4), 4
# (0, 1, 2, 3, 4)
[*range(4), 4]
# [0, 1, 2, 3, 4]
{*range(4), 4}
# {0, 1, 2, 3, 4}
{'x': 1, **{'y': 2}}
# {'x': 1, 'y': 2}
If you want to pass the last argument as a tuple of (mnt, False, bvar[0], bvar[1], ...) you could use
temp = self.treemodel.insert(iter, 0, (mht,False)+tuple(bvar) )
The extended call syntax *b can only be used in calling functions, function arguments, and tuple unpacking on Python 3.x.
>>> def f(a, b, *c): print(a, b, c)
...
>>> x, *y = range(6)
>>> f(*y)
1 2 (3, 4, 5)
Tuple literal isn't in one of these cases, so it causes a syntax error.
>>> (1, *y)
File "<stdin>", line 1
SyntaxError: can use starred expression only as assignment target
Not it isn't right. Parameters expansion works only in function arguments, not inside tuples.
>>> def foo(a, b, c):
... print a, b, c
...
>>> data = (1, 2, 3)
>>> foo(*data)
1 2 3
>>> foo((*data,))
File "<stdin>", line 1
foo((*data,))
^
SyntaxError: invalid syntax
You appear to have an extra level of parentheses in there. Try:
temp=self.treemodel.insert(iter,0,mht,False,*bvar)
Your extra parentheses are trying to create a tuple using the * syntax, which is a syntax error.

Categories