1

This is a follow up from my last question as to why the setattr within a class for a method did not work and I found an answer on it but now I have a small problem with it

class Test:
    def myString(self, var):
        Test.myString.string = var
        return self.myString

I then do this

x = Test()
x.myString("test").string

It returns test but then I try this

y = list()
[y.append(x.myString(str(i))) for i in range(10)]

then try to get the list by doing this, [j.string for j in y] and it returns 9 total digits of nine. Is their a way to fix this so it appends the number it's currently on?

thefourtheye
  • 233,700
  • 52
  • 457
  • 497
ruler
  • 613
  • 1
  • 10
  • 17

2 Answers2

1

This is a really weird design. I suspect there's a more natural way to do whatever this is trying to do.

That said, what's going on is that your myString method sets values on the class method itself. I see from your previous question (Can you set an attribute to a method in python) that calling the bound method creates a new instance of the bound method each time, so you are actually getting different objects returned from your successive calls to the myString method, but you're always updating a single value on the class's unbound method object. In general, if you're setting data on a class rather than an instance you're setting that data for all instances of the class.

I can't figure out what you're trying to do here. Obviously you could trivially get a list of the string representations of 0-9, but clearly there's something more required.

A much more natural design is just to set the string on the class instances - but it's so simple and straightforward it's actually hard to demonstrate.

class Test:
    pass

x = Test()
x.string = 'one'
print(x.string)

So, again, I can only assume you're looking for something more involved.

Community
  • 1
  • 1
Peter DeGlopper
  • 36,326
  • 7
  • 90
  • 83
  • I'm trying to get it to append 9 objects with the digits 0-9 like you said only I want to be able to call on them like `[variable.string for variable in listHere]` in my ass it would by `[j.string for j in y]` I do know however [str(i) for i in range(10)] would give me the same result but I am not after that – ruler Dec 01 '13 at 03:44
  • If you want 9 objects, where do you plan to instantiate the 9 different objects? You could easily do so in a list comprehension, but in your example code you just instantiate `x` once. That said, your posted code directly alters data on the class itself (`Test.myString.string`) so with this code it doesn't matter how many instances you have - they don't track `string` themselves, they all set it on the class. Which is part of the weirdness of the design. – Peter DeGlopper Dec 01 '13 at 03:53
  • I'm really struggling with why you want to have attributes on the method at all, as opposed to just member data in the object. – Peter DeGlopper Dec 01 '13 at 03:57
  • @ruler: Why do you want attributes on the method? That's a very weird place to put your data. – user2357112 Dec 01 '13 at 03:58
  • Hopefully this will help understand why I want this to work: http://pastebin.com/5rKWz168 – ruler Dec 01 '13 at 04:17
  • It's still pretty unclear - some kind of mapping of strings to objects created from the strings? A dict would be better than a list for that, even aside from the still open question of why the string belongs on a method. Is this an attempt at closures? – Peter DeGlopper Dec 01 '13 at 04:33
0

I found two ways to do what I needed here they are for anyone that was confused or didn't know how to do this either basically I wanted to create an entire new object rather than using the same one since when I called on it it used it's last data and thats why it didn't work before so using a lambda or class seemed to work fine but if anyone else has a different way of doing this let me know.

# Option One
class Test:
    def myString(self, string):
        newObject = lambda: None
        setattr(newObject,"string", string)
        return newObject
myTest = Test()
myList = list()
[myList.append(myTest.myString(str(i))) for i in range(10)]
[attr.string for attr in myList] # Output: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']

# Option Two
class newObject: pass

class Test:
    def myString(self, string):
        newObj = newObject()
        setattr(newObj,"string", string)
        return newObj
myTest = Test()
myList = list()
[myList.append(myTest.myString(str(i))) for i in range(10)]
[attr.string for attr in myList] # Output: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
ruler
  • 613
  • 1
  • 10
  • 17
  • I'm glad you figured out a solution that works for you, but this still makes no sense to me. These two approaches are both unnecessarily complex compared to just putting the strings directly into a list. Or if you want arbitrary attribute names other than `string`, save a dictionary for each string. That's basically all an empty class like your version two is. – Peter DeGlopper Dec 01 '13 at 06:07
  • @Peter Thanks, but yeah this is just some example code. The actual code makes sense for this type of stuff [see here](http://pastebin.com/Dm1yK9G1) I am aware that using the position then a list index would work for that also but I'd rather use names like obj.variable than listHere[number] – ruler Dec 01 '13 at 06:39
  • Does that pastebin code accurately represent your actual use case? It can be improved but there's not much point if it too is very simplified. – Peter DeGlopper Dec 01 '13 at 06:52