3. Guide to Python in CIV: Classes In Python

Civ IV relies heavily on the use of classes, so knowing how to use, manipulate and create them is important. As explained elsewhere, a class is simply created by using the "class Mod:" command, where the name of the class being created is "Mod". Functions are denoted with the "def ExFunction:" command, with "ExFunction" being its name. A function present in most classes is "init". This is a special function that is run the first time a class is called by CIV and never again (until the next time the program/mod is run). This is a good place to set up things that you want available later on without having to continually set them up.

Functions inside classes must always have at least one argument: "self". This refers to the class and allows you to use other parts of the class in that function. However, when CALLING a function you can ignore the argument. For example, a variable named "tempVar" would be created in a class' init function through the command "self.tempVar = x" with "x" being the data you want to store in it. The variable would be referenced in the class's functions in the same way: self.tempVar. The more you use classes (either already created or of your own design) to mod the more you will run across these types of references. To call other functions inside a class it works the same way. A function created through "def testFunction(self):" would be called from another function with the command "self.testFunction()." This is obviously not the way to call functions from other files or classes, but I will get to that shortly.

Functions always return something, that is, calling them gives you a value. For example, the calling of "Math().getPi()" may return 3.14. In other languages you can denote a function that you don't want to return something (i.e. you just want it to do something) by giving it the type Void, but that's not necessary in Python because as with variables, function types are stored internally. Inside of a function you can use a "return x" to exit the function and replace the function call with the value of "x", or simply say "return 0" to exit - though a return statement is not necessary if you don't want to use one.

Here's a short example showing the usage of the things I've already presented:

# File PyTestMod

# First Class
class TestClass:

        # Create First Variable
        TestVariable = "Test String"

        def __init__(self):
                # Change the variable
                self.TestVariable = "String Test"

        def TestFunction(self):
                self.TestVariable = self.FunctionTest()

        def FunctionTest(self):
                return "Wheeeeeee"

This does a few basic things: it creates a class named "TestClass" and an "instance" variable as it's called (meaning a variable belonging to a class) named "TestVariable" and sets it to the value of "Test String". When the "TestClass" is first called init is ran, which changes TestVariable to the value "String Test". At this point, the class is inactive, waiting for an outside call of one of its other functions. If the function "TestFunction" is called from outside, it sets the instance variable TestVariable equal to the value returned by the instance function FunctionTest, which is the string "Wheeeeeee". That's basically all there is to it.

Now then, calling functions and references variables within a class is fairly easy, but doesn't hold much power. Calling functions in other files and classes is usually much more important, as that is how programmers will have to access CIV data - you may be able to write a great class of your own, but without info from CIV it's not going to do much!

To access information and functions in another file, that file must first be imported. This is a simple task, completed through the use of the "import" command. If you have a file named PyTestMod, then to import it and its contents (classes and functions) you simply use the command "import PyTestMod". This allows you to call things within that file through "PyTestMod.blah", with "blah" being a function, class or variable in that file. However, if you plan on using the contents of this file extensively, you can take a shortcut and remove this step by saying "from PyTestMod import bleh". What this does is that it makes it so that you can do a straight call to whatever "bleh" is, be it a class, function or variable, without having to include the "PyTestMod." with it - which saves time and code. The drawback is that if you import a lot in this way it can be hard to track down where you're actually accessing things from, because there's no "PyTestMod." label in front of the references. To import everything in a file you use an asterisk: "from PyTestMod import *".

Let's return to the previous code example to illustrate how this works.

# Another File

class ClassX:

        import PyTestMod

        def __init__(self):
                PyTestMod.TestClass().TestFunction()

class ClassY:

        from PyTestMod import *

        def __init__(self):
                TestClass().TestFunction()

As you can see here, both classes function in the exact same way. However, the syntax is different, with the second class having less to type, which is advantageous. As warned above though, it's not easy to tell what file TestClass() comes from. Well, maybe it's not hard to track in this example, but when you get a lot more code flying around it can be.

I will now speak briefly about encapsulation, which fits in well with the current topic. Encapsulation means that certain parts (usually an import or a variable) of a file or class or function are only accessible 'locally', meaning within the context of the file/class/function you're working with. In the above example PyTestMod is imported twice - once in each class. This is necessary because each import call is "encapsulated" within each class - one call doesn't carry over. To make a single call carry over to both files you would use the import command above both classes within the "scope" (as it's called) of the entire file. Likewise, if you had used the import command inside one of the classes init functions, the import would only be valid within that single function, and if there were any other functions in that class you would need to import in those as well. Note though that an import or variable creation does not carry to HIGHER levels (you can't use an import in a function throughout the entire class), but it does carry over to LOWER levels - if you have an import in a class then all of the functions in that class "see" the contents of the import as well.

There is a way to cheat and make a variable or object usable throughout the entire class even if it's created within a function (and thus, normally out of the scope of the whole class). The command is "global" - "global MyGlobalVariable" for example, and is used occasionally in Civ IV.


Index

1. Guide to Python in Civ IV: Introduction
2. Guide to Python in Civ IV: Basic Python
3. Guide to Python in Civ IV: Classes in Python
4. Guide to Python in Civ IV: Python in Civ IV
5. Guide to Python in Civ IV: Iteration (Looping)
6. Guide to Python in Civ IV: Civ IV Function Assignment
7. Guide to Python in Civ IV: Creating a Unit in Civ IV
8. Guide to Python in Civ IV: Class Reference (by Locutus)
9. Further Class Reference (by Solver)

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License