Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python 3, list comprehensions, scope and how to compare against external variables

I have a class representing items of stock and their value:

class stock:
    def __init__(self, stockName, stockType, value):
        self.name = stockName
        self.type = stockType
        self.value = value

I have loads of stock so I make a list of stock objects from which I can access the value of individual items e.g stockList[12].value

I want to add up the value of all stock items which are 'shirts'. The following works fine:

shirtValue = sum([s.value for s in stockList if s.value == 'shirt'])

OK, fine. But now I have a list of stockType and wish to sum the value of items which match a particular entry in the stockType list e.g.:

stockTypeValue[0] = sum([s.value for s in stockList if s.value == stockType[0]])

where stockType[0] = 'shirt'This doesn't work. I know Why - it is because in Python 3 list comprehensions have their own scope (See detailed answer here: Accessing class variables from a list comprehension in the class definition )

My question is this: The code I have written, which I think would work in Python 2 looks great, it is clean, easy to understand, and to the untrained eye looks very pythonic.

But I can't figure out how to do the same thing in a nice pythonic way in Python 3. I'm going back to big loop structures.

What is the best way to do this in Python 3?

* Edit * Error messages:

Here is an example of what I am trying to do:

stockList=[]
stockList.append(stock('product A', 'shirt', 53.2))
stockList.append(stock('product B', 'hat', 20.2))

sum([s.value for s in stockList if s.type=='shirt'])

output:

Out[5]: 53.3

But if I put 'shirt' into a variable:

stockType = 'shirt'
sum([s.value for s in stockList if s.type==stockType])

output:

Traceback (most recent call last):
 File "<console>", line 1, in <module>
 File "<console>", line 1, in <listcomp>
NameError: name 'stockType' is not defined
like image 623
simonagill Avatar asked Oct 31 '22 07:10

simonagill


1 Answers

This doesn't work. I know Why - it is because in Python 3 list comprehensions have their own scope (See detailed answer here: Accessing class variables from a list comprehension in the class definition )

That’s actually not true. I mean, of course that answer in that question is correct but that’s not what is happening here. That question is only relevant for expressions that happen in the class body at definition time. If you are e.g. within a method though then there is no problem at all.

However, what likely fails (“likely” because you didn’t supply the error message), is that the target list stockTypeValue isn’t initialized.

>>> stockTypeValue[0] = 'something'
Traceback (most recent call last):
  File "<pyshell#4>", line 1, in <module>
    stockTypeValue[0] = 'something'
NameError: name 'stockTypeValue' is not defined

So we can define it now and assign an empty list; but the list is still empty:

>>> stockTypeValue = []
>>> stockTypeValue[0] = 'something'
Traceback (most recent call last):
  File "<pyshell#6>", line 1, in <module>
    stockTypeValue[0] = 'something'
IndexError: list assignment index out of range

Instead, you would have to append the result to the empty list:

>>> stockTypeValue.append('something')
>>> stockTypeValue[0]
'something'

Replaying your example:

>>> stockList = []
>>> stockList.append(stock('product A', 'shirt', 53.2))
>>> stockList.append(stock('product B', 'hat', 20.2))
>>> sum([s.value for s in stockList if s.type == 'shirt'])
53.2
>>> stockType = 'shirt'
>>> sum([s.value for s in stockList if s.type == stockType])
53.2
like image 142
poke Avatar answered Nov 15 '22 03:11

poke