Custom Classes
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 opshin.prelude
.
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
bytes
,int
, dicts, lists or other dataclasses.
Note that str
and None
are not valid field types of PlutusData.
Constructing objects
You can construct an object by calling the classname with the variables in order defined in the class.
a = Person(b"Billy", 1970)
Attribute access
All named attributes defined in the class body are accessible
by object.attribute
. For example, to access the name of a person we would run
print(a.name) # prints b"Billy"
Union types
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_ID
of Classes that occur in a Union to distinct values. On-Chain, classes are only distinguished by theirCONSTR_ID
value. If omitted, theCONSTR_ID
defaults to an almost-unique determinstic value based on the Class definition.
Type casts
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
.
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)
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)
You can also form the complement of type casts.
a: Union[Person, Animal] = ...
if isinstance(a, Person):
# a is of type Person in this branch
print(a.birthyear)
else:
# a is of type Animal in this branch
print(a.owner)
Note that you can also use
str
/print(a) # "Person(name=b'Billy', birthyear=1970)"
.to_cbor()
To obtain the CBOR representation of an object, you may call its to_cbor
method.
This will return the bytes
representing an object in CBOR.
print(a.to_cbor().hex())
# prints "d8799f4542696c6c791907b2ff"