This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| script:walkthrough:records [2017-03-20 20:26] – skyjake | script:walkthrough:records [2019-11-23 19:38] (current) – skyjake | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| + | <- Exceptions ^ ^ Functions -> | ||
| + | ====== Records ====== | ||
| + | |||
| + | |||
| + | Records are the foundation of Doomsday Script. Each namespace and module is a record, each object is a record, and classes are records, too. | ||
| + | |||
| + | The ownership model of records is that there is always a single owner for a record, and if that owner gets destroyed, all records owned by it will get destroyed, too. Many records are owned by native code (e.g., built-in modules like '' | ||
| + | |||
| + | A script variable may own a record, or simply reference one owned by someone else. When a record gets destroyed, all variables that reference it will be invalidated. In other words, records are __not__ reference-counted (like Python objects would be). | ||
| + | |||
| + | In practice, a record is a set of named variables. Some of these variables may point to other records, while others have plain data values. | ||
| + | |||
| + | ===== Creation ===== | ||
| + | |||
| + | There are a few alternative ways to create a record: | ||
| + | * '' | ||
| + | * Built-in '' | ||
| + | * '' | ||
| + | |||
| + | With the '' | ||
| + | |||
| + | record a, b | ||
| + | |||
| + | Note that the '' | ||
| + | |||
| + | The '' | ||
| + | |||
| + | a = Record() | ||
| + | b = Record(a) | ||
| + | |||
| + | '' | ||
| + | |||
| + | Record({' | ||
| + | |||
| + | '' | ||
| + | |||
| + | (record myrec).expressionCreated = True | ||
| + | myrec.(record subexp).alsoExpCreated = True | ||
| + | |||
| + | The member operator '' | ||
| + | |||
| + | $ record myrec | ||
| + |  | ||
| + | $ myrec.newMember = 100 | ||
| + |  | ||
| + | $ print len(myrec) | ||
| + | 1 | ||
| + |  | ||
| + | $ print myrec.newMember | ||
| + | 100 | ||
| + |  | ||
| + | $ print myrec | ||
| + | newMember: | ||
| + | |||
| + | Subrecords can be created with the '' | ||
| + | |||
| + | $ record myrec | ||
| + |  | ||
| + | $ record myrec.subrec | ||
| + |  | ||
| + | $ myrec.subrec.a = 1 | ||
| + |  | ||
| + | $ print myrec | ||
| + | subrec.a:1 | ||
| + | |||
| + | ===== Members ===== | ||
| + | |||
| + | The '' | ||
| + | |||
| + | $ print ' | ||
| + | ' | ||
| + | ' | ||
| + | True True False | ||
| + | |||
| + | The built-in functions '' | ||
| + | |||
| + | record myrec | ||
| + | myrec.var = ' | ||
| + | myrec.(record subrec).a = 123 | ||
| + | print ' | ||
| + | print ' | ||
| + | |||
| + | Output: | ||
| + | |||
| + |  | ||
| + | subrecords: { subrec: a:123 } | ||
| + | |||
| + | Members of a record can also be accessed with the '' | ||
| + | |||
| + | print myrec[' | ||
| + | print myrec[' | ||
| + | print myrec[' | ||
| + | print members(myrec)[' | ||
| + | |||
| + | Members can also be created or changed using '' | ||
| + | |||
| + | myrec[' | ||
| + | |||
| + | ===== Aliasing ===== | ||
| + | |||
| + | When assigning a record to another variable, the variable references the same record but doesn' | ||
| + | |||
| + | record myrec | ||
| + | myrec.firstMember = 123 | ||
| + | reference = myrec | ||
| + | reference.otherMember = 5 | ||
| + | print " | ||
| + | print myrec | ||
| + | |||
| + | Output: | ||
| + | |||
| + | Here's myrec: | ||
| + | firstMember: | ||
| + | otherMember: | ||
| + | |||
| + | ===== Deletion ===== | ||
| + | |||
| + | The '' | ||
| + | |||
| + | del a | ||
| + | del myrec.subrec.something | ||
| + | |||
| + | If the variable owns a record, the record will be destroyed as well. | ||
| + | |||
| + | record soonDeleted | ||
| + | reference = soonDeleted | ||
| + | del soonDeleted | ||
| + | try: print reference | ||
| + | catch Error, er: print er | ||
| + | |||
| + | Output: | ||
| + | |||
| + | [NullError] (in RecordValue:: | ||
| + | |||
| + | ===== Serialization ===== | ||
| + | |||
| + | Records can be serialized with '' | ||
| + | |||
| + | record myrec | ||
| + | myrec.a = 1 | ||
| + | print ' | ||
| + | print ' | ||
| + | print deserialize(serialize(myrec)) | ||
| + | |||
| + | Output: | ||
| + | |||
| + | serialize(myrec) = (Block of 29 bytes) | ||
| + | deserialize(serialize(myrec)) = | ||
| + | a:1 | ||
| + | |||
| + | ===== Classes ===== | ||
| + | |||
| + | A class is a record that acts as the namespace containing the members of the class. Classes can define an initializer method that gets called during instantiation. | ||
| + | |||
| + | record MyClass() | ||
| + | a = 'class member' | ||
| + | def __init__() | ||
| + | self.b = 'local member' | ||
| + | end | ||
| + | end | ||
| + |  | ||
| + | obj = MyClass() | ||
| + | print obj.a | ||
| + | print obj.b | ||
| + | |||
| + | Output: | ||
| + | |||
| + | class member | ||
| + | local member | ||
| + | |||
| + | Note that there is nothing special about a class — it is simply a record that contains variables, just like any other record. In fact, the only special thing about class instantiation is that behavior of the member operator ('' | ||
| + | |||
| + | This means that, for instance, an imported module could be used as a class. | ||
| + | |||
| + | Setting up the superclass relationship and calling the initializer method ''< | ||
| + | |||
| + | The special variable '' | ||
| + | |||
| + | Arguments can be passed to the initializer method. | ||
| + | |||
| + | record MyClass() | ||
| + | def __init__(someVar) | ||
| + | self.v = someVar | ||
| + | end | ||
| + | end | ||
| + |  | ||
| + | obj = MyClass(' | ||
| + | print obj.v | ||
| + | |||
| + | Output: | ||
| + | |||
| + | arg | ||
| + | |||
| + | ==== Inheritance ==== | ||
| + | |||
| + | A subclass is a class that inherits from an another class. | ||
| + | |||
| + | Normally member lookup proceeds to check through each superclass in order, however it is also possible to manually specify the namespace for identifier lookup. This is done with the ''< | ||
| + | |||
| + | record MyClass() | ||
| + | def __init__(someVar) | ||
| + | self.v = someVar | ||
| + | end | ||
| + | end | ||
| + |  | ||
| + | record Subclass(MyClass) | ||
| + | def __init__() | ||
| + | self.MyClass-> | ||
| + | end | ||
| + | end | ||
| + |  | ||
| + | obj = Subclass() | ||
| + | print obj.v | ||
| + | |||
| + | Output: | ||
| + | |||
| + | from sub | ||
| + | |||
| + | The scope notation can be used sequentially. For example, if the parent class is part of another record: | ||
| + | |||
| + | record t.MyClass() | ||
| + | |||
| + | Now in Subclass, the initializer needs to looked up with a sequence of scopes: | ||
| + | |||
| + | self.t-> | ||
| + | |||
| + | The behavior of this statement is: | ||
| + | - Look up //self//. | ||
| + | - The member operator '' | ||
| + | - Look up //t//. | ||
| + | - Within //t//, look up // | ||
| + | - Within // | ||
| + | - Make a function call on //< | ||
| + | |||
| + | Note that the scope notation does not restrict the lookup to superclasses; | ||
| + | |||
| + | Compare this with: | ||
| + | |||
| + | # This is wrong: | ||
| + | self.t.MyClass.__init__(' | ||
| + | |||
| + | This doesn' | ||
| + | * //t// is in the global namespace, so it's not found within //self//. | ||
| + | * Even if that somehow worked, //< | ||