If you want to define a custom class to be used in Opshin, it must be a
dataclass which inherits from the
PlutusData class which can be imported from
from opshin.prelude import * @dataclass() class Person(PlutusData): # Every person has a UTF8 encoded name name: bytes # Every person has a year of birth birthyear: int
PlutusData may contain only
int or other dataclasses.
You can construct an object by calling the classname with the variables in order defined in the class.
a = Person(b"Billy", 1970)
All named attributes defined in the class body are accessible
object.attribute. For example, to access the name of a person we would run
print(a.name) # prints b"Billy"
It may happen that you allow more than a single type of data for your application (think of a Smart Contract that allows different operations on it).
In this case, you may define a
Union[A, B, ...] type.
This expresses that a variable may be of either of the classes inside the square brackets.
@dataclass() class Plant(PlutusData): CONSTR_ID = 1 # Plants have no further properties @dataclass() class Animal(PlutusData): CONSTR_ID = 2 # Animals have a name too! name: bytes # They also have an owner, which is another dataclass owner: Person # Note all of these classes have distinct CONSTR_ID values CityDweller = Union[Animal, Plant, Person] # Both assignments are fine, because a is annotated # to be of the Union type and can be of either class c: CityDweller = Plant() c = Animal(b"jackie", a)
Importantly, you need to set the
CONSTR_IDof Classes that occur in a Union to distinct values. On-Chain, classes are only distinguished by their
CONSTR_IDvalue. If omitted, the
CONSTR_IDdefaults to 0.
If a variable is of an Union type we may still want to distinguish how we handle them
based on the actual type.
For this, we can use the function
isinstance(x, A) returns
True if value
x is an instance of class
A (which is not a Union type!).
# this is forbidden! # If a is a Plant or Animal, it does not have a birthyear so this operation will fail. print(a.birthyear) if isinstance(a, Person): # Here its okay # OpShin recognizes the isinstance check and knows that # a is of type Person in this branch of the condition print(a.birthyear)
New in 0.16.0: We can combine isinstance calls and access shared attributes across classes.
if isinstance(a, Person) or isinstance(a, Animal): # a is of type Union[Person, Animal] in this branch # Both classes have the same attribute at the same position # so we can access it in either case print(a.name)
Note that you can also use
print(a) # prints "Person(name=b'Billy', birthyear=1970)"
To obtain the CBOR representation of an object, you may call its
This will return the
bytes representing an object in CBOR.
print(a.to_cbor().hex()) # prints "d8799f4542696c6c791907b2ff"