Help accessing a Class's Variables from a Function within the Class in a smooth way

Hi!

I have a hard time understanding classes, what they are good for and how to use them. In this post i tried to use classes, but i bumped into some fancy problems, i solved some myself but this one has been bothering me everytime i have been trying to use classes. Why i can’t access variables inside the class from a function inside the class?

Example code:


class main():
    classvariable = "I want to be accessed from the function below!"
    def testFunction():
        print(classvariable)
main.testFunction()

That will not work i know, but how do i make it work without using arguments like


class main():
    classvariable = "I will be accessed from the function below!"
    def testFunction(classvariable):
         print(classvariable)
main.testFunction(main.classvariable)

and


class main():
    classvariable = "I will be accessed from the function below!"
    global classvariable
    def testFunction():
        global classvariable
        print(classvariable)
main.testFunction()

The code above works and prints the right string, but how do i access the variables in a easy way without using the methods i showed above? It gets quite clumsy when the script gets bigger and bigger :eek:

“”""""""""

Object Oriented Programming (hereafter referred to as OOP), is a whole new approach to Programming. I will not go into detail of OOP because there is much to say about it and yet I have not much time. Here is a resource that you will find very useful:https://www.tutorialspoint.com/python/python_classes_objects.htm

it would do you good to research python classes some more. classes are very useful, since its a data set that can be persistent, and stored anywhere.

basic structure: (assume a game object has a python module pointing to “text.SLIDE”, or “text.SPIN”)


## text.py ##

from bge import logic, events

class BASE:

    CLVAR = "my value is shared in all BASE classes"

    def __init__(self):
        ## runs once when class is created ##
        self.variable = "my value is unique to 'self' class"
        self.spawner = logic.getCurrentController().owner
        thisClass = self
        self.STARTUP()

    def STARTUP(self):
        SCENE = logic.getCurrentScene()
        self.owner = SCENE.addObject("Cube", self.spawner, 0)
        self.owner["RUN"] = self #save self to the game object property "RUN"

    def RUN(self):
        print(self.owner, ": Using BASE class!!")

class SPIN(BASE):

    def RUN(self): # overwrite this module from BASE, inherit the rest
        self.owner.applyMovement((0, 0.1, 0), True)

class SLIDE(BASE):

    def RUN(self):
        self.owner.applyRotation((0, 0, 0.1), True)


def RUNCLASS(): # Game object "Cube" has logic python module pointing to "text.RUNCLASS"
    OWNER = logic.getCurrentController().owner

    OWNER["RUN"].RUN() # run the class module RUN


i dont think i missed anything…

EDIT: to access variables from the class:

## Assuming part of RUNCLASS module ##

print(OWNER["RUN"].CLVAR, OWNER["RUN"].variable)

@Fillelind,
If you want that to work without using global variable, you must decorate the function with @classmethod and then python will “forcefully inject” the class object into the function, so you must capture it like this; func(cls)


class main():
    classvariable = "I want to be accessed from the function below!"
    @classmethod
    def testFunction(cls):
        print(cls.classvariable)
main.testFunction()

tl:dr, class variable is only available to class method or instance method

Could it be you are coming from Java?

I’m not sure if you really are looking for a class variable or an object variable (a variable of a specific class instance).

Class Variable


class ClassWithClassVariable():
    classVariable = "class variable content"

print(ClassWithClassVariable.classVariable)

Please notice: The qualifier is the class name (without parenthesis)

Use cases:

Define constants that belong to the class


class ObjectWithMode():
    MODE_WAITING = 0
    MODE_WALKING = 1
    MODE_RUNNING = 2

    def __init__(self):
        self.mode = self.MODE_WAITING 

    def switchToMode(self, mode):
        self.mode = mode
                    

object = ObjectWithMode()
object.switchToMode( ObjectWithMode.MODE_WALKING ) # by class
object.switchToMode( object.MODE_WALKING ) # the object inherited the class variable

This is pretty much the same as defining the constants in a module. I recommend to use the class name as qualifier (the “by class” line)

Processing over all instances of a class
Imagine you want to know the number of instantiated classes (not necessarily the currently existing instances). You could implement a usage counter:



class ObjectWithCounter():
    instancesCreated = 0

    def __init__(self):
        ObjectWithCounter.instancesCreated += 1

                    
print(ObjectWithCounter.instancesCreated) # ask the class -> should be 0

object = ObjectWithCounter()
print(object.instancesCreated) # ask the instance  -> should be 1

anotherObject = ObjectWithCounter()
print(object.instancesCreated) # ask another instance -> should be 2

ObjectWithCounter()
print(ObjectWithCounter.instancesCreated) # ask the class again  -> should be 3

Class variables are shared a across all instances of that class (even sub classes). This is not the typical use case as this guaranties side effects. Just in case you mean object variables here is how to use them:

Object variables

Object variables belong to the instance of the class. Different instances do not know about the variables of the other.


class ObjectWithName():
    def __init__(self, name):
         self.name = name

    def __repr__(self):
        return "I'm {0} of type {1}".format(
            self.name, type(self))

jamesBond = ObjectWithName("Jame Bond")
mrBean = ObjectWithName("Mr. Bean")
print(jamesBond)
print(mrBean)

Please notice the qualifier “self” in front of the variable.

Thank you Nicholas_A, guramarx and Monster!
No didn’t come from Java, i just had a hard time understanding how to use the classes.

After some testing i figured out how to use “init(self)” and got it to work the way i wanted by using self everywhere. The code i am using can now access the class’s init function’s variables by using self.


class main():
    
        def __init__(self):
             self.cont = bge.logic.getCurrentController()
             self.key = bge.logic.keyboard.events
             self.own = self.cont.owner
             self.scene = bge.logic.getCurrentScene()
             self.rotContRot = self.scene.objects[self.own["Child"]]
             self.zRot = self.own["zRot"]
             self.rot = self.own.localOrientation.to_euler()
             self.rotRot = self.rotContRot.localOrientation.to_euler()
             self.WKEY = self.key[bge.events.WKEY]
             self.SKEY = self.key[bge.events.SKEY]
             self.AKEY = self.key[bge.events.AKEY]
             self.DKEY = self.key[bge.events.DKEY] 

        # Compare the Rotation of the Owner compared to the Rotation Controller
        # I should probably make the code below into a function and use it the same
        # way as movement(self) further down the code
             if self.rot[0] < self.rotRot[0]:
                 self.rot[0] += 0.02
             if self.rot[0] > self.rotRot[0]:
                 self.rot[0] -= 0.02
             if self.rot[1] < self.rotRot[1]:
                 self.rot[1] += 0.02
             if self.rot[1] > self.rotRot[1]:
                 self.rot[1] -= 0.02
             if self.rot[2] < self.rotRot[2] and self.zRot != 2:
                 self.rot[2] += 0.02
                 if self.rot[2] < self.rotRot[2]:
                      self.own["zRot"] = 1
                 else:
                     self.own["zRot"] = 0
             if self.rot[2] > self.rotRot[2] and self.zRot != 1:
                 self.rot[2] -= 0.02
                 if self.rot[2] > self.rotRot[2]:
                     self.own["zRot"] = 2
                 else:
                     self.own["zRot"] = 0
             self.own.localOrientation = self.rot.to_matrix()
         # Function for moving the owner
         def movement(self):
             if self.WKEY == 2:
                 if self.own.getVelocity()[0] > -25:
                     self.own.applyForce([15,0,0], True)
             if self.SKEY == 2:
                 if self.own.getVelocity()[0] < 10:
                     self.own.applyForce([-15,0,0], True)
             if self.AKEY == 2:
                 if self.own.getVelocity()[1] > -10:
                     self.own.applyForce([0,15,0], True)
             if self.DKEY == 2:
                 if self.own.getVelocity()[1] < 10:
                     self.own.applyForce([0,-15,0], True)
                
main().movement()

I am not creating objects and the only way to make my module work is to have the game continuosly reload the module. Would it be good/more correct if i would make the module use objects and not being forced to continuosly reload the module? When i look at my code right now it feels like i am using classes and functions for no reason at all. The module is just running over and over again anyways so i could as well skip classes and functions and only have code in the module?

To understand classes think of them like this:
Every time you do “MyClass()”, if MyClass is a class (and not a function) an “object” is created. This object is like a copy of the class. If you don’t use pharenthesis, and do “MyClass.something” you’re accessing the original object, if you do “MyClass().something; MyClass().somethingelse()” you’re actually creating 2 copies of the class. So what you want to do is “obj = MyClass(); obj.something(); obj.somethingelse()”. When you use “self”, what you’re doing is accessing not the original class but whatever copy the method has been called from, so if I do “my_object1.doit()”, inside “doit”, the variable “self” will be “my_object1”, if I do “my_object2.doit()” now inside “doit” the variable “self” will be “my_object2” and if inside “doit” I use “MyClass” I’ll be accessing the original object.

(I’ve taken some licenses on the last explanation but oh well)

Here some tips:

  • Use uppercase for the first letter of a class name, it’s a very uselful non written rule that enables you to distinguish at a glance what is an object/variable/function of what is a class.
  • Don’t use a “Main” class, your main code should not be a class, and except on languages like java where everything is a class in python, you don’t need it.
  • When you do “main().movement()” you’re creating a new object, that’s not what you want to do, what you want to do is use the object of your last frame. So on the first frame you do “my_object = MyClass()” and on every other frame you do “my_object.movement()”.
  • It may be a better idea to use more generic names like “update” or “loop” since a movement is something very specific and there may be cases where you want to do more than that. Then you can call “self.movement()” from inside your “def update(self):” method if you want.
  • You should not reload the module every frame, that’s designed to debug not to fix a broken design.
  • A “non-tested” example of initialization cold be like this:

(To use with the module mode on the Python Controller Logic Brick)


import bge

class MyClass:#All your class here, though it would be x9 times better if you use "import", an x9 times better than that if you import an external file and use another IDE to code.


def __init__(self):
[INDENT=2]...
[/INDENT]

def update(self):

[INDENT=2]...
[/INDENT]

scene = bge.logic.getCurrentScene()
my_objects = []

for obj in scene.objects: my_objects.append(MyClass(obj))

 
#Everything before this will be called only in the first frame
def loop(): #You call this from the Logic Brickfor x in my_objects:

[INDENT=2]x.update()

[/INDENT]

It is good.