I was practising the following exercise of Codality lessons in Python:
Your goal is to find the maximal product of any triplet.
Write a function:
def solution(A)
that, given a non-empty array A, returns the value of the maximal product of any triplet.
For example, given array A such that:
A[0] = -3
A[1] = 1
A[2] = 2
A[3] = -2
A[4] = 5
A[5] = 6
the function should return 60, as the product of triplet (2, 4, 5) is maximal.
def solution(A):
A.sort()
N=len(A)
P1=A[N-1]*A[0]*A[1]
P2=A[N-1]*A[N-2]*A[N-3]
return max(P1, P2)
print(solution[4,2,2,5,1,5,8])
I have face this error while running the code : TypeError: 'function' object is not subscriptable
Can you please help me out in fixing the code?
print(solution([4,2,2,5,1,5,8]))
This is what you're missing
These added () is what tells the Python interpreter:
"Get what the solution() function returns when given [4,2,2,5,1,5,8] as the parameter.
Writing something like solution[something] means to subscript, or 'take some part out of' the solution, which is applicable to strings or lists for example, but not to functions, hence the error.
You should write it as solution([A]) and not solution[A].
I am a C programmer trying to learn Python - still a newbie.
def fx(x, y):
return x, y
a, b = fx(1, 2)
fx() must be returning x and y as part of a data structure - perhaps a list or a tuple. Exactly what it is I need to know.
Thanks for any inputs,
Manish Jain
There's no such thing as multiple return values in Python, and nothing is happening implicitly. You are constructing a tuple and returning it. x, y is a valid expression creating a 2-tuple. If you don't believe me, open up a Python REPL and type 1, 2; you'll get the tuple (1, 2) as a result.
It is a tuple, and to verify this for yourself you can just do:
a = fx(1, 2)
print(type(a))
Outputs:
<class 'tuple'>
You are making a tuple and returning it.
From the docs:
Tuples of two or more items are formed by comma-separated lists of
expressions.
a = [[0]*3] * 4
print(a[0] is a[1]) # True
when I initialize a two dimensional array that way, things went wrong.
Took me a bit time to find this unexpected behavior. So is this syntax only work on immutable object?
It "worked" in your example too, in its way. This is just how the implementation of list.__mul__ interprets what you want. The list can't construct new objects, it doesn't know how to create new objects from whatever objects it happens to contain. It expands itself with new references to those objects.
You get the same behavior with immutable integers
>>> x = [0] * 3
>>> x[0] is x[1]
True
You can get the two dimensional array with
>>> a = [[0]*3 for _ in range(4)]
>>> a[0] is a[1]
False
The reason why [0] * 3 does what you want is that it is creating list that contains 3 references to the same immutable 0.
What went"wrong"?
It does make a two-dimensional list. (I don't know if that's a thing but I mean to say it makes the list in a form where you can convert it to a 2-D array).
To convert it to a proper array, you will have to use
import numpy as np
a = [[0]*3] * 4
a = np.array(a)
You can make this immutable by converting it to a tuple.
If we take b = [1,2,3] and if we try doing: b+=(4,)
It returns b = [1,2,3,4], but if we try doing b = b + (4,) it doesn't work.
b = [1,2,3]
b+=(4,) # Prints out b = [1,2,3,4]
b = b + (4,) # Gives an error saying you can't add tuples and lists
I expected b+=(4,) to fail as you can't add a list and a tuple, but it worked. So I tried b = b + (4,) expecting to get the same result, but it didn't work.
The problem with "why" questions is that usually they can mean multiple different things. I will try to answer each one I think you might have in mind.
"Why is it possible for it to work differently?" which is answered by e.g. this. Basically, += tries to use different methods of the object: __iadd__ (which is only checked on the left-hand side), vs __add__ and __radd__ ("reverse add", checked on the right-hand side if the left-hand side doesn't have __add__) for +.
"What exactly does each version do?" In short, the list.__iadd__ method does the same thing as list.extend (but because of the language design, there is still an assignment back).
This also means for example that
>>> a = [1,2,3]
>>> b = a
>>> a += [4] # uses the .extend logic, so it is still the same object
>>> b # therefore a and b are still the same list, and b has the `4` added
[1, 2, 3, 4]
>>> b = b + [5] # makes a new list and assigns back to b
>>> a # so now a is a separate list and does not have the `5`
[1, 2, 3, 4]
+, of course, creates a new object, but explicitly requires another list instead of trying to pull elements out of a different sequence.
"Why is it useful for += to do this? It's more efficient; the extend method doesn't have to create a new object. Of course, this has some surprising effects sometimes (like above), and generally Python is not really about efficiency, but these decisions were made a long time ago.
"What is the reason not to allow adding lists and tuples with +?" See here (thanks, #splash58); one idea is that (tuple + list) should produce the same type as (list + tuple), and it's not clear which type the result should be. += doesn't have this problem, because a += b obviously should not change the type of a.
They are not equivalent:
b += (4,)
is shorthand for:
b.extend((4,))
while + concatenates lists, so by:
b = b + (4,)
you're trying to concatenate a tuple to a list
When you do this:
b += (4,)
is converted to this:
b.__iadd__((4,))
Under the hood it calls b.extend((4,)), extend accepts an iterator and this why this also work:
b = [1,2,3]
b += range(2) # prints [1, 2, 3, 0, 1]
but when you do this:
b = b + (4,)
is converted to this:
b = b.__add__((4,))
accept only list object.
From the official docs, for mutable sequence types both:
s += t
s.extend(t)
are defined as:
extends s with the contents of t
Which is different than being defined as:
s = s + t # not equivalent in Python!
This also means any sequence type will work for t, including a tuple like in your example.
But it also works for ranges and generators! For instance, you can also do:
s += range(3)
The "augmented" assignment operators like += were introduced in Python 2.0, which was released in October 2000. The design and rationale are described in PEP 203. One of the declared goals of these operators was the support of in-place operations. Writing
a = [1, 2, 3]
a += [4, 5, 6]
is supposed to update the list a in place. This matters if there are other references to the list a, e.g. when a was received as a function argument.
However, the operation can't always happen in place, since many Python types, including integers and strings, are immutable, so e.g. i += 1 for an integer i can't possibly operate in place.
In summary, augmented assignment operators were supposed to work in place when possible, and create a new object otherwise. To facilitate these design goals, the expression x += y was specified to behave as follows:
If x.__iadd__ is defined, x.__iadd__(y) is evaluated.
Otherwise, if x.__add__ is implemented x.__add__(y) is evaluated.
Otherwise, if y.__radd__ is implemented y.__radd__(x) is evaluated.
Otherwise raise an error.
The first result obtained by this process will be assigned back to x (unless that result is the NotImplemented singleton, in which case the lookup continues with the next step).
This process allows types that support in-place modification to implement __iadd__(). Types that don't support in-place modification don't need to add any new magic methods, since Python will automatically fall back to essentially x = x + y.
So let's finally come to your actual question – why you can add a tuple to a list with an augmented assignment operator. From memory, the history of this was roughly like this: The list.__iadd__() method was implemented to simply call the already existing list.extend() method in Python 2.0. When iterators were introduced in Python 2.1, the list.extend() method was updated to accept arbitrary iterators. The end result of these changes was that my_list += my_tuple worked starting from Python 2.1. The list.__add__() method, however, was never supposed to support arbitrary iterators as the right-hand argument – this was considered inappropriate for a strongly typed language.
I personally think the implementation of augmented operators ended up being a bit too complex in Python. It has many surprising side effects, e.g. this code:
t = ([42], [43])
t[0] += [44]
The second line raises TypeError: 'tuple' object does not support item assignment, but the operation is successfully performed anyway – t will be ([42, 44], [43]) after executing the line that raises the error.
Most people would expect X += Y to be equivalent to X = X + Y. Indeed, the Python Pocket Reference (4th ed) by Mark Lutz says on page 57 "The following two formats are roughly equivalent: X = X + Y , X += Y". However, the people who specified Python did not make them equivalent. Possibly that was a mistake which will result in hours of debugging time by frustrated programmers for as long as Python remains in use, but it's now just the way Python is. If X is a mutable sequence type, X += Y is equivalent to X.extend( Y ) and not to X = X + Y.
As it's explained here, if array doesn't implement __iadd__ method, the b+=(4,) would be just a shorthanded of b = b + (4,) but obviously it's not, so array does implement __iadd__ method. Apparently the implementation of __iadd__ method is something like this:
def __iadd__(self, x):
self.extend(x)
However we know that the above code is not the actual implementation of __iadd__ method but we can assume and accept that there's something like extend method, which accepts tupple inputs.
I am taking my first python course, this should be an easy thing but I can't get it right. I have done a search, but I guess the keyword are just too common.
So, I did an assignment where I had to write a function that squares a number. I did it successfully with just:
def square(x):
'''x: int or float.'''
return x * x
I have tried with other functions just to try different ways of doing the exercise, if I try to incorporate some loop like:
def square(x):
'''x: int or float.'''
for number in range(x):
result = x * x
return result
It only works for integers(so square(5) gives me 25, but square (5.0) gives me 'TypeError: range() integer end argument expected, got float'. How can I get this function to square floats or negative numbers? Thanks in advance
First question
It's only valid for integers(so square(5) gives me 25, but square (5.0) gives me 'TypeError: range() integer end argument expected, got float'.
Answer
Because range function is defined as taking only integer data. See Python documentation.
Example:
>>> range(5)
[0, 1, 2, 3, 4]
>>> range(5.0)
TypeError: range() integer end argument expected, got float.
>>> range("5")
TypeError: range() integer end argument expected, got str.
>>> range(0, 5)
[0, 1, 2, 3, 4]
>>> range(0, 5.0)
TypeError: range() integer end argument expected, got float.
Second question
How can I get this valid for floats or negative numbers?
Answer
I don't know what "valid" means. It depends on what you are trying to do. So please comment or update your question.
The loop in your second example doesn't actually do anything: it overwrites the value of result every time with the same number, and doesn't even use the loop counter.
If you want an alternate way to compute a square, a method for integers only that uses loops would be this:
def square2(x):
x = int(math.fabs(x))
result = 0
for i in range(x):
result += x
return result
This takes the sum of x added together x times (i.e., converts the multiplication into addition). It handles negative integers as well, but not floats. The reason this doesn't work for floats is that you can't count from 0 to an arbitrary float (real numbers are uncountable).
Honestly, your first solution in your question is the straightforward, simple, and therefore (IMO) the best one.