This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
script:walkthrough:records [2017-03-13 18:13] – 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, //< |