• 📚 A new project from the admin: Check out PictureBooks.io, an AI storyteller that lets you create personalized picture books for kids in seconds. Give it a try and let me know what you think!

Why self? [PYTHON]

Joined
Jul 5, 2004
Messages
23,562
Location
Canberra, Australia
In all the examples I have seen when a method is defined for a class the first parameter is self but when the method is called this parameter is not passed. However when I wrote some code PYTHON tells me I need to pass the instance to the method?

Code:
TypeError: unbound method setDefaultSubdueAnimalChance() must be called with dhzUnitEnslavement instance as first argument (got int instance instead)

Just in case it helps I have attached the code. It is for BUG 3.6.
 
The self parameter is indeed the instance of the class (object) that you are calling the method (function) on. In Python you can refer to a function by name and assign the function to a variable. In fact, whenever you refer to a function to call it you are doing this without knowing it.

Code:
[B]> def foo(x):
>     print "x = ", x[/B]

[B]> bar = foo
> print bar[/B]
<function blah blah>

[B]> bar(2)[/B]
x = 2

"bar" now holds a function object just as "foo" does. In fact they hold the same function object.

You can refer to a class's function the same way as it is just another function. When you call a function like this normally, you do so through an object of that class.

Code:
[B]> class Car:
>     def drive(self, d):
>         print "driving %d miles" % d[/B]

[B]> c = Car()
> c.drive(5)[/B]
driving 5 miles

Above, "c.drive" binds the function drive() to the object "c". This creates a new bound function object.

Code:
[B]> c.drive[/B]
<bound function blah blah>

You can also refer to the class function without binding it to an object.

Code:
[B]> myDrive = Car.drive
> myDrive[/B]
<unbound function blah blah>

To call myDrive you need to provide the Car instance that you want to use for "self" in the drive() function.

Code:
[B]> myDrive(c, 10)[/B]
driving 10 miles

If you omit it as you have done, you get the error you got. It is telling you that you are calling an unbound function and must pass in an instance.
 
@EmperorFool, Thank you for a clear response to my question. However I think I asked the wrong question in the first place :). I am going to have to think on what my question actually was because I took what you said above changed some code and now none of the functions exist.

old code
Code:
		global enslavementMod
		enslavementMod = dhzUnitEnslavement

new code
Code:
		global enslavementMod
		enslavementMod = dhzUnitEnslavement()

result
Code:
Traceback (most recent call last):

  File "CvEventInterface", line 30, in onEvent

  File "BugEventManager", line 298, in handleEvent

  File "BugEventManager", line 303, in _dispatchEvent

  File "BugEventManager", line 315, in _handleDefaultEvent

  File "dhzEnslavementEventManager", line 76, in onCombatResult

NameError: global name 'enslavementMod' is not defined
ERR: Python function onEvent failed, module CvEventInterface

So obviously my understanding of python needs some serious improvement.
 
The change you made is correct, but it looks like it's not being called. Are you perhaps loading a saved game instead of starting a new one? In that case you'll need an onLoad handler. Best to move the code that's in onGameStart to a new function such as initEnslavement and call that function from both onGameStart and onLoad.
 
You can save stuff in the Script Data of various objects (game, player, team, unit, city, plot, map). To make this manageable and work with multiple mods, use Stone-D's Advanced Toolkit (it's in BUG's Python/Contrib folder).
 
I wish there was an easier way because I don't understand script data very well (and Stone-D's Advanced Toolkit even less) so I'm limited to one string per object in the game. I attached the stuff to players for one scenario. I hope it works fine, because if it doesn't I've just introduced a game-breaking bug into it and it will probably never be known (new players would never know it wasn't working, especially since I can't get them to turn python exceptions on, and old ones have since moved on; I can't test it because of the long time it takes to get to the point in the scenario where this data is used).
 
Stone-D's toolkit is actually pretty easy to use. The module itself has cryptic documentation, but a working example might be easier. BUG's Reminder mod uses it to store the reminders in the game object. You'll only need to use about 3 or 4 functions.
 
The change you made is correct, but it looks like it's not being called. Are you perhaps loading a saved game instead of starting a new one? In that case you'll need an onLoad handler. Best to move the code that's in onGameStart to a new function such as initEnslavement and call that function from both onGameStart and onLoad.

You are right. Also I need the onLoad anyway don't I, if I want the rules anyway. Now I need to figure out why this is not playing nicely with OrionVetran's Inquasition mod. At least I have an idea why it isn't :)
 
If the object that you create in onGameStart() doesn't need to be recreated when they start/load a new game, you could use BUG's <init> configuration feature to have it created once.

dhzEnslavement.xml

Code:
[B]<init module="dhzEnslavementEventManager"/>[/B]
<events .../>

dhzEnslavementEventManager.py

Code:
class dhzEnslavementEventManager:
    ...

[B]def init():
    global enslavementMod
    enslavementMod = dhzUnitEnslavement()[/B]

Then you ditch the onLoad() and onGameStart() functions. But you need to make sure that the dhzUnitEnslavement object doesn't keep state about the current game. If it does, you need to stick to what you're doing.

When you run your mod, turn on BUG debugging in the options screen (set File Log Level to DEBUG). Then you will see in Logs/PythonDbg.log as BUG calls your <events/> initialization. You can also activate event logging in init.xml (look for BugEventManager). This will print out as each event is fired and what it's calling.
 
OK, went away, thought hard, got rid of a few typo type bugs :).

1. Not sure I fully understand your comments about using the BUG <init> tag. This mod has some rules which are defined once then used throughout the game and not changed. So yes using the <init> tag looks like the way to go. If I do the <init> do I still need the <load>? Or do I need <events>? Your doco on the interaction between the tags is not clear to me, even though the explanations of the individual tags is good.

2. Now I seem to be down to one problem which I think may be a unbounded/bounded problem but I am not sure.

I am getting this error
Code:
  File "dhzUnitEnslavement", line 341, in addRule

AttributeError: ruleForEnslavement instance has no __call__ method

Which is happening on this line
Code:
		self.rules.append(newRule())

Which is preceded by

Code:
	def __init__(self) :
		self.rules = list()  # create a list to hold the rules

	def addRule(self, .....):
		
<snip validate >...

		newRule = ruleForEnslavement()
<snip set values>...
		self.rules.append(newRule())

Obviously I am not understanding how the list() stuff works as I am going from Zebra9's code. I think I just changed the names of the variables so I could understand the code. Also I only have doco for Python version 3 not 2.3.
 
This mod has some rules which are defined once then used throughout the game and not changed. So yes using the <init> tag looks like the way to go. If I do the <init> do I still need the <load>? Or do I need <events>? Your doco on the interaction between the tags is not clear to me, even though the explanations of the individual tags is good.

You still use <load> in init.xml to tell BUG to load your configuration XML file. Then in that file where you have used <events> you put before that line the <init> tag.

<init> tells BUG to call a function once during initialization. This is where you setup your rules. <events> tells BUG to hookup your event manager. I typically put <events> after <init> in case the code in the event manager requires the variables created in the <init> function. In that case you need to tell BUG to call you <init> function immediately (by default BUG calls them all at the end of initialization) using the "immediate" attribute. Your event manager doesn't need it at this time, so you don't need this attribute.

Code:
<init .../>
<events .../>

Where ... specifies your module name as I showed above.

AttributeError: ruleForEnslavement instance has no __call__ method

...

Code:
		newRule = ruleForEnslavement()
<snip set values>...
		self.rules.append(newRule())

Here newRule is being set to the result of instantiating the ruleForEnslavement class. Remove the () on the following line.

Code:
self.rules.append(newRule[s]()[/s)
 
Excellent, I now have two thirds of this mod working - on to piracy! Unfortunately when I made the changes to use <init> everything fell into a heap as it said I did not have an init function and that BUG init was already running. I will look at it again while I do the piracy stuff, at times dyslexia sucks :)
 
Argh! I think I now have everything working OK but thought some trace prints to make sure wouldn't go astray but I can't find them. The traces that is.

Where does
Code:
		BugUtil.debug("dhzEnslavementEventManager.__init__()  initializing. g_bUseEnslaveMod is %s." %(g_bUseEnslaveMod))

go?
 
In dhzEnslavementEventManager.__init__()?

Code:
# dhzEnslavementEventManager.py

...

class dhzEnslavementEventManager:

    def __init__():
        ...
        BugUtil.debug("dhzEnslavementEventManager.__init__()  initializing. g_bUseEnslaveMod is %s." %(g_bUseEnslaveMod))

...
 
Ah, the logging statements in BugUtil go to PythonErr.log and/or the screen depending on your logging level settings. Go to the System tab of the BUG Options Screen. If you set the level to Debug, it will print everything.
 
Back
Top Bottom