I will attempt to do my best to explain. Yes, __new__ gets called when the object is created, and allows us to manipulate object creation. __new__ is also a "class" method, while __init__ is an "instance" method.
When you override a method, and then call super(<class>, <instance>).__<method>__(), Python goes step by step through the hierarchy of the class to see which class in the hierarchy has a method of that name. So, for the current example, we call __new__ and in that method, we call the super class' __new__. Since PlayerEntity is the class that we are inheriting from, Python first checks that class. PlayerEntity does not directly have a __new__ built into it. So, Python checks PlayerEntity's inherited class BaseEntity. BaseEntity does in fact have a __new__ method, so that is the method that gets called. BaseEntity's __new__ method is designed to help us create any BaseEntity object, verify that the given index is of a valid edict, and stores the entity's index and edict as private attributes (which are made available to us via public properties):
Syntax: Select all
class BaseEntity(object):
'''Class used to interact directly with entities'''
def __new__(cls, index, *entities):
'''Override the __new__ class method to verify the given index
is of the correct entity type and add the index attribute'''
# Get the given indexes edict
edict = CEdict(index)
# Is the edict valid?
if edict.is_free() or not edict.is_valid():
# If not raise an error
raise ValueError(
'Index "{0}" is not a proper entity index'.format(index))
# Create the object
self = object.__new__(cls)
# Set the entity's base attributes
self._index = index
self._edict = edict
self._entities = frozenset(list(entities) + ['entity'])
# Return the instance
return self
@property
def index(self):
'''Returns the entity's index'''
return self._index
@property
def edict(self):
'''Returns the entity's edict instance'''
return self._edict
@property
def entities(self):
'''Returns the set of entity names to use for the instance'''
return self._entities
The "entities" property (and _entities private attribute) are used by BaseEntity to know which folders to use when checking properties, keyvalues, offsets, and functions (all of which use in the ../data/source-python/ directory).
In PlayerEntity, again, we do not override __new__, but instead use __init__. We only use this to store the PlayerInfo object of the player (after verifying it is a valid PlayerInfo object) and make sure the "entities" property is only entity and player:
Syntax: Select all
class PlayerEntity(BaseEntity, _PlayerWeapons):
'''Class used to interact directly with players'''
def __init__(self, index):
'''Override the __init__ method to set the
"entities" attribute and set the PlayerInfo'''
# Set the player's info attribute
self._info = CPlayerInfo(self.edict)
# Is the IPlayerInfo instance valid?
if self.info is None:
raise ValueError(
'Invalid IPlayerInfo instance for index "{0}"'.format(index))
# Set the entities attribute
self._entities = frozenset(['entity', 'player'])
@property
def info(self):
'''Returns the player's IPlayerInfo instance'''
return self._info
If you do not call the super __new__ method after getting the index, the object will not be created properly, and will give you errors. If you do not call the super __init__ method, the PlayerInfo will not be stored and the entities property will not be the correct value. With the way things are setup in PlayerEntity/BaseEntity, you "must" override both methods.
Having said all of this, I now realize how easy it would be for me to just override __new__ within PlayerEntity instead of __init__. I am going to test my changes before I commit them, but possibly here soon, you will not have to override __init__ and call the super() method.
I hope you understood that, if not, please feel free to ask questions.
Satoon