Ren'Py How to best use the __getitem__() (Solved)

GNVE

Active Member
Jul 20, 2018
724
1,185
I felt like doing a little bit of coding rather than working on my current projects. So I decided to be overly ambitious and start doing things waaaay outside of my python skill level :ROFLMAO:

I ran into a problem trying to call info from a class so I can use it in a screen. After a couple of hours of googling and experimentation I understand I have to use the __getitem__ dundermethod.
I just wanted to know if what I'm doing is the best way to do it or if there is a better way. As you can see it will become a very large function with a class like this. The examples online all use really small classes as an example to keep it simple but in this case maybe a little to simple.
Python:
class business:
        def __init__(self, name, description, status, wealth, cost, LT, base, mod, store, stats, hourleave, houropen, hourclose, dayopen, workers, workercorrupt, workermax, upgrades):
            self.name = name  #string
            self.description = description  #tuple of strings
            self.status = status  #string
            self.wealth = wealth #lib
            self.cost = cost #integer
            self.LT = LT  #string
            #etc...
           
        def __getitem(self,key):
            if key == 'name':
                return self.name
            elif key == 'description':
                return self.description
            elif key == 'status':
                return self.status
            #etc...
(I'm sure similar questions will arise when I start looking into __setitem__ :cry:)
 
Last edited:

HELIO CESAR

Active Member
May 30, 2018
719
1,514
it's generally this way to go, if i would comment one thing it would be to define getters and setters methods for each attribute, suppose you want the last 'item' of the possibles 'item', there will be a myriad of if tests until we get our wanted item, and perhaps you can better define the data structure, let's say create an item class, and the business class has an attribute list of available items.
 
  • Like
Reactions: GNVE

Satori6

Game Developer
Aug 29, 2023
503
1,117
getattr is what you're looking for.

Python:
class business:
        def __init__(self, name):
            self.name = name

         
        def getproperty(self,key):
            return getattr(self,key)

b=business('test')
print(b.getproperty('name')) #test
 
  • Like
Reactions: GNVE

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,979
16,236
I ran into a problem trying to call info from a class so I can use it in a screen. After a couple of hours of googling and experimentation I understand I have to use the __getitem__ dundermethod.
Hmm, why ?

__getitem__() is used with tupples, lists, sets and dictionaries, to access a given item on the "list". At no time this have an use in basic objects. And at no time should you define the method in them, unless you want to overwrite the object behavior ; what would be even more above your python skill level.

The code you show make me thing about an error I saw recently, and seem to point to an addressing error, using object["attribute"] to address and attribute, in place of the expected object.attribute.


(I'm sure similar questions will arise when I start looking into __setitem__ :cry:)
Well, same issue, same solution: object.attribute = new value
 
  • Like
Reactions: GNVE

GNVE

Active Member
Jul 20, 2018
724
1,185
it's generally this way to go, if i would comment one thing it would be to define getters and setters methods for each attribute, suppose you want the last 'item' of the possibles 'item', there will be a myriad of if tests until we get our wanted item, and perhaps you can better define the data structure, let's say create an item class, and the business class has an attribute list of available items.
Believe it or not this has already been condensed. for instance base and mod are libraries of base values and their modifiers for instance.

getattr is what you're looking for.

Python:
class business:
        def __init__(self, name):
            self.name = name

      
        def getproperty(self,key):
            return getattr(self,key)

b=business('test')
print(b.getproperty('name')) #test
Thanks, that was more in line with what I was expecting.
Hmm, why ?

__getitem__() is used with tupples, lists, sets and dictionaries, to access a given item on the "list". At no time this have an use in basic objects. And at no time should you define the method in them, unless you want to overwrite the object behavior ; what would be even more above your python skill level.

The code you show make me thing about an error I saw recently, and seem to point to an addressing error, using object["attribute"] to address and attribute, in place of the expected object.attribute.




Well, same issue, same solution: object.attribute = new value
It was the literal error I was getting back from Ren'Py that lead me down this path (TypeError: 'business' object has no attribute '__getitem__') Online tutorials seem to indicate this is a valid method when used for classes.
but yup I had the ['attribute'] rather then .attribute. It has been a while since I have tried to code a class.
 
Last edited:

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,979
16,236
It was the literal error I was getting back from Ren'Py that lead me down this path (TypeError: 'business' object has no attribute '__getitem__') Online tutorials seem to indicate this is a valid method when used for classes.
I don't know what tutorials you looked, but object["attribute"] is not, have never been, and will surely never be, a valid method to address objects' attributes. It's either object.attribute, or possibly object.__dict__["attribute"], but that's all.

And for the error, it's a bit counter intuitive, but it's Python trying to tell you that you don't address the value in the right way. It have to be read as "It's not how it should be done, because that object have no '__getitem__' magic method".



By the way...
Python:
class business:
        def __init__(self, name):
            self.name = name
      
        def getproperty(self,key):
            return getattr(self,key)

b=business('test')
print(b.getproperty('name')) #test
Why ? Why something that is both over complicated and totally useless ?

I don't say that because the issue was the way he address the attributes, but because there's strictly and absolutely no need for that getproperty method. print( getattr( b, 'name') ) would do the same, and directly.

At most, the answer would be:
Python:
class Whatever( object ): # or ( renpy.python.RevertableObject )
        def __init__(self, name):
            self.name = name
      
        def __getitem__(self,key):
            return getattr(self,key)

        def __setitem__(self,key,value):
            return setattr(self,key,value)

b = Whatever( "It's me, Joey" )
print( b["name"] )
b["name"] = "It's not me, Joey"
But as I said, it's useless because print( b.name ) and b.name = "whatever" would do exactly the same, without the need to define the two magic methods.


The __setitem__ and __getitem__ methods are only needed when you want your class to act as an hybrid. Like by example an event class that would act both as a dictionary, a list and an object:
Python:
class Event( renpy.python.RevertableObject ):
    [...]

    #  Return True/False depending if the game context match the condition 
    # and then trigger the event.
    def triggered( self ):
        [...]

    #  Process the event
    def __call__( self ):
        [...]

class EventHandler( renpy.python.RevertableObject ):
    def __init__( self, [...] ):
        self.__events = {}

   def add( self, eID, event ):
       if eID in self.__events: raise ValueError( "'{}' event already defined.".format( eID ) )
       [whatever other computation are needed]
       self.__events[eID] = event

   def remove( self, eID ):
       if not eID in self.__events: raise ValueError( "'{}' event do not exist.".format( eID ) )
       [whatever other computation are needed]
       del self.__events[eID]

   def iter( self ):
        for eID in self.__events.keys():
            # Return only the triggered events
            if self.__events[eID]:
                yield self.__events[eID]

   def __getitem__( self, eID ):
       return self.__events[eID]  

   def __getattr__( self, eID )
            if not eID in self.__events:   raise AttributeError( "'{}' object has no attribute '{}'".format( self.__class__.__name__, eID ) )
            return self.__event[eID]

   # Force the use of the /add/ and /remove/ methods
   __setitem__ = add
   __delitem__ = remove
   __setattr__ = add
   __delattr__ = remove


h = EventHandler()
h.add( "test", Event( [...] )
h["another"] = Event( [...] )
h.dirty = Event( [...] )
for e in h:
    [...]
if h["test"].triggered():
    [...]
if h.test():
    [...]
 

Satori6

Game Developer
Aug 29, 2023
503
1,117
Why ? Why something that is both over complicated and totally useless ?

I don't say that because the issue was the way he address the attributes, but because there's strictly and absolutely no need for that getproperty method. print( getattr( b, 'name') ) would do the same, and directly.
Because that's what matches what the OP posted.

I don't know what else they're planning to do with the properties when they're retrieved, but there must be a reason for that. Else they'd just do b.name
 

GNVE

Active Member
Jul 20, 2018
724
1,185
What it boils down to is that I went down the wrong rabbit hole :) I have worked with classes before but that was a couple of years ago so I am beyond rusty and not very skilled at that.

As stated: So I decided to be overly ambitious and start doing things waaaay outside of my python skill level :ROFLMAO: