How do you assign one value to multiple variables

2019-07-24 15:16发布

问题:

I have begun to take my first steps in learning python and am trying to start using classes.

I have this simplified version so far:

class ThingsInTheFridge(object):
  def __init__(self):
    self.milk = False
    self.yogurt = False
    self.bread = True
    self.umbrella = True

There will be about 30 things in my class and each is assigned True or False (I have shortened the list for this question).

What is the most efficient way to assign them?

I have thought about this but it doesn't seem to improve things especially when the list increases in size:

self.milk, self.yogurt = False, False

EDIT: I perhaps should have mentioned there are other items in the class to (I omitted them because I didn't think it would matter):

class ThingsInTheFridge(object):
  def __init__(self):
    self.milk = False
    self.yogurt = False
    self.bread = True
    self.umbrella = True
    self.bulb = "maker name"
    self.shoes = 2

回答1:

If you really care about runtime efficiency, you're almost certainly looking in the wrong place. If initializing your members takes too long (which is very unlikely… but if you profile and find this is the hotspot…), the right answer is to use __slots__, not to change the way you do the initialization.

If you care about programmer efficiency (readability, and to a lesser extent writability), you can always do this:

self.milk = self.yogurt = False

If you want to do things more dynamically, you could do something like this with setattr, but I wouldn't recommend it:

class ThingsInTheFridge(object):
    false_things = ('bread', 'umbrella')
    true_things = ('milk', 'yogurt')
    def __init__(self):
        for thing in ThingsInTheFridge.false_things:
            setattr(self, thing, False)
        for thing in ThingsInTheFridge.true_things:
            setattr(self, thing, True)

The reason I wouldn't recommend it is that, if you need to do things dynamically at this level, you also probably need to be dynamic at the access level, which means you really should be using a dict or set, as explained by Lattyware.

You could also go overboard and build yourself a prototype-style OO system on top of the class-style system, or create a metaclass that initializes instances based on class information, etc., but, as the start of this sentence indicates, that would be going overboard.



回答2:

I think this displays a problem with your model of storing the data. You probably want to be using a data structure like a set, not just attributes.

For example:

items = {"milk", "yogurt", "bread", "umbrella"}
stored = set()

Then just store the ones you want in the stored set. Sets have very fast performance for membership checks, so to check, you can simply do "milk" in stored, for example.

If you really wanted to keep the interface you have, you could use __getattr__() to override the action you get upon asking for an attribute:

def ___getattr__(self, item):
    return item in self.stored

This will return False to anything not in the fridge, if you needed it to only respond to possible items, that could be easily done:

def ___getattr__(self, item):
    if item not in self.items:
        raise AttributeError
    return item in self.stored

Naturally, setting can be done too:

def __setattr__(self, item, value):
    if item in self.items:
        if value:
            self.stored.add(item)
        else:
            self.stored.remove(item)
    else:
        ...

An alternative to this would be a dict to True/False - I can't really say I think either is particularly better, one might suit your project more. Just use whatever works best for you.



回答3:

I think your main focus should be readability and the underlying data structure.

Something like this is very readable and (likely) as high performance as you can achieve by rolling you own:

from collections import Counter

class Fridge(object):
    def __init__(self,things,name):
        self.things=Counter(things)
        self.wanted=set()
        self.name=name

    def look(self, thing):
        print 'There are {} {} in {}'.format(self.things[thing],thing,self.name)

    def stare_inside(self):
        cathave=sorted([(co,it) for it,co in self.things.items() if co])
        print '{} items in {}:'.format(len(cathave),self.name)
        for i, item in enumerate(cathave,1):
            print '   {}: {} -- {}'.format(i,item[1],item[0])

    def shop(self):
        shop_list=[it for it,co in self.things.items() if co==0]
        for item in shop_list:
            del self.things[item]
        shop_list.extend(list(self.wanted))
        self.wanted=set()
        print "shopping list:"
        for i, item in enumerate(sorted(shop_list),1):
            print '   {}: {}'.format(i,item)    

    def consume(self,thing,count):
        if self.things[thing]>=count:
            self.things[thing]-=count   
        else: 
            print 'Not enough {} to consume {} -- {} in the {}.'.format(
                  thing,count,self.things[thing],self.name)
            self.wanted.add(thing)

    def stock(self,things):
        self.things+=Counter(things)

Now try it:

>>> kitchen=Fridge({'coke':2, 'whole milk':1,'ketchup':1, 'anchovies':24},'kitchen fridge')
>>> kitchen.look('coke')
There are 2 coke in kitchen fridge
>>> kitchen.stock({'coke':1,'pepsi':2,'banana':3})
>>> kitchen.stare_inside()
6 items in kitchen fridge:
   1: ketchup -- 1
   2: whole milk -- 1
   3: pepsi -- 2
   4: banana -- 3
   5: coke -- 3
   6: anchovies -- 24
>>> kitchen.consume('red bull',22)
Not enough red bull to consume 22 -- 0 in the kitchen fridge.
>>> kitchen.consume('coke',3)
>>> kitchen.consume('whole milk',1)
>>> kitchen.stare_inside()
4 items in kitchen fridge:
   1: ketchup -- 1
   2: pepsi -- 2
   3: banana -- 3
   4: anchovies -- 24
>>> kitchen.shop()
shopping list:
   1: coke
   2: red bull
   3: whole milk

This is based on the collection module Counter class. You are unlikely to come up with something that is native Python that is faster.



回答4:

You might actually be better off using a set of strings instead of individual flag members. Something like the following might work better for what you seem to be trying to do.

class ThingsInTheFridge:
    """I represent a collection of items in a refrigerator.

    I contain a few things by default.  You can add and remove items as
    well as test for membership.

    >>> things = ThingsInTheFridge()
    >>> object() in things
    False
    >>> things.store_item('foo')
    >>> 'foo' in things
    True
    """
    def __init__(self):
        self._items = set()
        self.store_item('bread')
        self.store_item('umbrella')
    def store_item(self, item):
        """Add an item to the fridge.
        :param item: the item to add.

        Items are coerced into strings and normalized before they are
        added to the internal list.

        >>> things = ThingsInTheFridge()
        >>> things.store_item(False)
        >>> 'false' in things
        True
        >>> 'FALSE' in things
        True
        >>> False in things
        True
        """
        self._items.add(str(item).lower())
    def __contains__(self, item):
        """Check if an item is in the fridge.
        :param item: item to check for.

        Items are coerced into strings and normalized to match the processing
        in :py:meth:`store_item`.

        >>> things = ThingsInTheFridge()
        >>> things.store_item('Milk')
        >>> 'Milk' in things, 'milk' in things, 'MiLk' in things
        (True, True, True)
        """
        return str(item).lower() in self._items