Is there a way to grab list attributes that have been initialized using self and append data to them in Python?

Question:

I have a class in Python that initializes the attributes of an environment. I am attempting to grab the topographyRegistry attribute list of my Environment class in a separate function, which when called, should take in the parameters of ‘self’ and the topography to be added. When this function is called, it should simply take an argument such as addTopographyToEnvironment(self, "Mountains") and append it to the topographyRegistry of the Environment class.
When implementing what I mentioned above, I ran into an error regarding the ‘self’ method not being defined. Hence, whenever I call the above line, it gives me:

    print (Environment.addTopographyToEnvironment(self, "Mountains"))
                                                  ^^^^
NameError: name 'self' is not defined

This leads me to believe that I am unaware of and missing a step in my implementation, but I am unsure of what that is exactly.
Here is the relevant code:

class EnvironmentInfo:
    def __init__(self, perceivableFood, perceivableCreatures, regionTopography, lightVisibility):
        self.perceivableFood = perceivableFood
        self.perceivableCreatures = perceivableCreatures
        self.regionTopography = regionTopography
        self.lightVisibility = lightVisibility

class Environment:
    def __init__(self, creatureRegistry, foodRegistry, topographyRegistery, lightVisibility):
        logging.info("Creating new environment")
        self.creatureRegistry = []
        self.foodRegistry = []
        self.topographyRegistery = []
        self.lightVisibility = True

    def displayEnvironment():
        creatureRegistry = []
        foodRegistry = []
        topographyRegistery = ['Grasslands']
        lightVisibility = True
        print (f"Creatures: {creatureRegistry} Food Available: {foodRegistry} Topography: {topographyRegistery}  Contains Light: {lightVisibility}")

    def addTopographyToEnvironment(self, topographyRegistery):
        logging.info(
            f"Registering {topographyRegistery} as a region in the Environment")
        self.topographyRegistery.append(topographyRegistery)

    def getRegisteredEnvironment(self):
        return self.topographyRegistry

if __name__ == "__main__":
        print (Environment.displayEnvironment())                         #Display hardcoded attributes
        print (Environment.addTopographyToEnvironment(self, "Mountains"))#NameError
        print (Environment.getRegisteredEnvironment(self))               #NameError

What am I doing wrong or not understanding when using ‘self’?

Edit: In regard to omitting ‘self’ from the print statement, it still gives me an error indicating a TypeError:

 print (Environment.addTopographyToEnvironment("Mountains"))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: Environment.addTopographyToEnvironment() missing 1 required positional argument: 'topographyRegistery'
Asked By: Homerian

||

Answers:

Self represents the instance of the class and you don’t have access to it outside of the class, by the way when you are calling object methods of a class you don’t need to pass self cause it automatically be passed to the method you just need to pass the parameters after self so if you want to call an object method like addTopographyToEnvironment(self, newVal) you should do it like:

Environment.addTopographyToEnvironment("Mountains")

and it should work fine

Answered By: alireza.2281

Comments

  • Despite having def getRegisteredEnvironment(self): it wasn’t indented, so it’s not recognized as a class method.
  • self is a keyword used in conjunction with classes (class methods or attributes) – not functions. self is implied to be the instantiated object (eg a = Environment(...) -> self would refer to a) or the module’s (I can’t think of the proper term) class.
  • You didn’t have your addTopographyToEnvironment class method defined.
  • In terms of your Environment class, you aren’t using the variables you are passing to the class, so I made that change as well – I don’t know if that was intentional or not.
  • As per your comment from the other answer, if you had def my_class_method(self) and you try to invoke it through an object with additional parameters, like so a = my_object(); a.my_class_method("Mountains"), you should get an error of the sorts, "2 positional arguments passed, expected 1.".
  • Your main problem is that you are doing Environment.class_method() and not creating an object from the class. Do a = Environment(whatever arguments here) to create an object from the class, then do a.addTopographyToEnvironment("Mountains") to do what you were going to do with "Mountains" and that object. What you have currently may be right, its just is missing the proper implementation, but the below article does a great job explaining the differences between all of them (Class Methods vs Static Methods vs Instance Methods), and is definitely worth the read.
class EnvironmentInfo:
    def __init__(self, perceivableFood, perceivableCreatures, regionTopography, lightVisibility):
        self.perceivableFood = perceivableFood
        self.perceivableCreatures = perceivableCreatures
        self.regionTopography = regionTopography
        self.lightVisibility = lightVisibility

class Environment:
    def __init__(self, creatureRegistry, foodRegistry, topographyRegistery, lightVisibility):
        logging.info("Creating new environment")
        self.creatureRegistry = creatureRegistry
        self.foodRegistry = foodRegistry
        self.topographyRegistery = topographyRegistery
        self.lightVisibility = lightVisibility

    def displayEnvironment(self):
        creatureRegistry = []
        foodRegistry = []
        topographyRegistery = ['Grasslands']
        lightVisibility = True
        print (f"Creatures: {creatureRegistry} Food Available: {foodRegistry} Topography: {topographyRegistery}  Contains Light: {lightVisibility}")

    def addTopographyToEnvironment(self, environment):
        return "Whatever this is supposed to return." + environment

    def getRegisteredEnvironment(self):
        return self.topographyRegistry

if __name__ == "__main__":
        print (Environment.displayEnvironment())                         #Display hardcoded attributes
        print (Environment.addTopographyToEnvironment("Mountains"))#NameError
        print (Environment.getRegisteredEnvironment())               #NameError

Object Instantiation In Python

With all that out of the way, I will answer the question as is posed, "Is there a way to grab list attributes that have been initialized using self and append data to them in Python?". I am assuming you mean the contents of the list and not the attributes of it, the attributes would be "got" or at least printed with dir()

As a simple example:

class MyClass:
    def __init__(self, my_list):
        self.my_list = my_list


if __name__ == "__main__":
    a = MyClass([1, 2, 3, 4, 5])
    print(a.my_list)
    # will print [1, 2, 3, 4, 5]
    a.my_list.append(6)
    print(a.my_list)
    # will print [1, 2, 3, 4, 5, 6]
    print(dir(a.my_list))
    # will print all object methods and object attributes for the list associated with object "a".

Sub Classing In Python

Given what you have above, it looks like you should be using method sub classing – this is done with the keyword super. From what I can guess, it would look like you’d implement that kind of like this:

class EnvironmentInfo:
    def __init__(self, perceivableFood, perceivableCreatures, regionTopography, lightVisibility):
        self.perceivableFood = perceivableFood
        self.perceivableCreatures = perceivableCreatures
        self.regionTopography = regionTopography
        self.lightVisibility = lightVisibility

class Environment(EnvironmentInfo):
    def __init__(self, creatureRegistry, foodRegistry, topographyRegistery, lightVisibility, someOtherThingAvailableToEnvironmentButNotEnvironmentInfo):
        logging.info("Creating new environment")
        super.__init__(foodRegistry, creatureRegistry, topographyRegistery, lightVisibility)
        self.my_var1 = someOtherThingAvailableToEnvironmentButNotEnvironmentInfo

    def displayEnvironment(self):
        creatureRegistry = []
        foodRegistry = []
        topographyRegistery = ['Grasslands']
        lightVisibility = True
        print (f"Creatures: {creatureRegistry} Food Available: {foodRegistry} Topography: {topographyRegistery}  Contains Light: {lightVisibility}")

    def addTopographyToEnvironment(self, environment):
        return "Whatever this is supposed to return." + environment

    def getRegisteredEnvironment(self):
        return self.topographyRegistry

    def methodAvailableToSubClassButNotSuper(self)
        return self.my_var1

if __name__ == "__main__":
    a = Environment([], [], [], True, "Only accessible to the sub class")
    print(a.methodAvailableToSubClassButNotSuper())

as the article describes when talking about super(), methods and attributes from the super class are available to the sub class.


Extra Resources

Class Methods vs Static Methods vs Instance Methods – "Difference #2: Method Defination" gives an example that would be helpful I think.

What is sub classing in Python? – Just glanced at it; probably an okay read.

Answered By: Shmack
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.