1

My question is how to create variables "on the fly". I'm trying to generate an object with a random set of attributes.

from random import randint, choice

class Person(object):
    def __init__(self):
      self.attributes = []    

possible_attributes= ['small', 'black', 'scary', 'smelly', 'happy'] # idk, random
chance = randint(1,5)

chosen_attributes = []
for i in xrange(chance):
    #psuedo code...
    local_var = choice(possible_attributes)
    if local_var not in chosen_attributes:
        VAR = local_var # VAR needs to be dynamic and 'global' for use later on
        chosen_attributes.append(local_var)
        Person.attributes.append(local_var)

I'm positive how I'm wanting to do this is not a good way, so if anyone understand what I'm looking for and can offer a better method (one that works, for starters) I would be most appreciative.

martineau
  • 119,623
  • 25
  • 170
  • 301
jtsmith1287
  • 1,110
  • 2
  • 13
  • 24
  • 3
    It's... pretty rotten. Use a dictionary instead. – Ignacio Vazquez-Abrams Aug 15 '12 at 04:19
  • Why do you need VAR to be 'global'? What's wrong with building a Person instance that has all of the desired attributes? – Walter Mundt Aug 15 '12 at 04:20
  • I think the issue is you need to figure out how to avoid this. Dictionaries and lists do a wonderful job. – Snakes and Coffee Aug 15 '12 at 04:24
  • 3
    The reason people say this is bad is this: if you don't know the name of the variable you're creating, how are you going to later retrieve its value from the unknown variable? You'll be forced to use a similar "lookup a dynamically chosen variable" technique. But instead, why not use a dictionary in a variable with a *known* name, whose keys can be unknown? – Ben Aug 15 '12 at 04:24
  • Wow, my question was down-voted, despite spending several hours trying to figure this out, and finally resorting to asking on here... not very encouraging to ask questions on this site... – jtsmith1287 Aug 15 '12 at 04:48
  • 1
    @jtsmith1287: you also got 6 suggested answers to your question, plus a link to a related question with two more. I'd hope that would be enough reason to stick around. While I didn't downvote you, there is one important thing you could do to improve your question and make it less likely to be downvoted: add more context about what you are trying to do and why. Active users on here really don't like questions where they feel unable to give the best answer for lack of enough information in the question, and I certainly felt limited in that way while writing my answer below. – Walter Mundt Aug 15 '12 at 17:54

6 Answers6

2

To add attributes to an instance of the class you can use .__dict__:

class Person(object):
    def addattr(self,x,val):
        self.__dict__[x]=val

output:

>>> a=Person()
>>> a.addattr('foo',10)
>>> a.addattr('bar',20)
>>> a.foo
10
>>> a.bar
20
>>> b=Person()
>>> b.addattr('spam','somevalue')
>>> b.spam
'somevalue'
Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
2

Well Im not sure what you are tying to achieve here, but there might be a couple things off here ....

class Person(object):
    def __init__(self):
        self.attributes = [] 

This defines a class object from which you can create objects that contain attributes as we can see from the __init__ or constructor as its called in some other languages.

but you have

Person.attributes.append(local_var)

here Person is a class object it really doesn't have the attributes that where define since does are only for objects created from this class ...

you can create a new person and add the appropriate attributes to them.

>>> me = Person()
>>> me.attributes.append('humble')

Second we really shouldn't access attributes directly since anything could be appended to a list, including duplicate attributes.

so why not simply add a function that adds attributes.

class Person(object):
    def __init__(self):
        self._attributes = [] # note any variable starting with _ is meant to be private 
    def add_attribute(attribute):
        if attribute not in self._attributes:
            self._attributes.append(attribute)

if performance is a concern we can use a set() instead of [] since it won't allow duplicate entries and really fast checks for any lookups the downside is that the values have to be hash-able ie strings or ints or other simple data types ...

Samy Vilar
  • 10,800
  • 2
  • 39
  • 34
  • Right. It was just psuedo code. I was trying to be brief, but had I not known, thanks for taking the time to inform me! :) I kept getting ragged on in other posts about not being brief, and so I made an effort this time, and in return got down voted for posting something that doesn't look good... I'm probably going to just avoid posting here from now on. (I apologize for the random rant. I'm grumpy now) – jtsmith1287 Aug 15 '12 at 05:11
1

Use vars. For example:

>>> import math
>>> class foo: pass
>>> vars(foo)
{'__module__': '__main__', '__doc__': None}
>>> vars(foo)['fn']=math.sin
>>> foo.fn(2)
0.9092974268256817
>>> vars(foo)
{'__module__': '__main__', '__doc__': None, 'fn': <built-in function sin>}

As you know, methods are just functions with one more argument.

KAction
  • 1,977
  • 15
  • 31
1

While I can't really tell what you need this for, I am going to guess that you don't actually want global variables, just a single Person object that has a random/dynamic set of attributes. For that, you want setattr and random.sample():

crazy_person = Person()
selected_attribute_names = random.sample(possible_attributes, 3)
for attribute_name in selected_attribute_names:
    setattr(crazy_person, attribute_name, True)

# optional -- if you want to have a list of attributes, just copy that in:
crazy_person.attributes = selected_attribute_names
# vars(crazy_person).keys() will give you more or less the same thing though, for free

# hasattr(crazy_person, 'small') will tell you if you picked 'small'
# if we chose 'small', then from here on, crazy_person.small will be True as well
Walter Mundt
  • 24,753
  • 5
  • 53
  • 61
0

This is how I might construct objects with a random set of attributes, given your definitions:

import random

possible_attributes= ['small', 'black', 'scary', 'smelly', 'happy']

class Person(object):
    def __init__(self):
      self.attributes = random.sample(possible_attributes, random.randint(0, len(possible_attributes)))

This selects a random number of attributes from your list of possible attributes.

>>> print Person().attributes
['small', 'smelly']
>>> print Person().attributes
['scary', 'smelly', 'happy']
Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
0

Generally, instead of polluting the global namespace, you do not want to add to globals. Dictionaries provide a namespace-like way to access everything, but if you really have to, use globals()[x] =something to modify the item, or perhaps use the very nasty

exec "var_name=somehting"
Snakes and Coffee
  • 8,747
  • 4
  • 40
  • 60
  • 1
    I would highly suggest _against_ using `exec`, unless you _really_ - no, **really** - know why you need it. – voithos Aug 15 '12 at 04:22
  • 1
    Fact: `exec` slows down your whole program because Python has to turn off a bunch of optimisations it can otherwise do. – Greg Hewgill Aug 15 '12 at 04:25