In wc_rules, we are essentially constructing graph representations of chemical entities. The basic building blocks of the graph are
To build this graph, one needs to know about :
Objects such as molecules, sites and interactions are derived from generic base classes provided in wc_rules. To create various types of these objects, one can simply subclass them as often as needed.
Below, we create the type hierarchy
Molecule -> A -> A1where A is a subclass of molecule and A1 is a subclass of A
In [1]:
    
from wc_rules.chem2 import Molecule,Site
class A(Molecule):
    pass
class A1(A):
    pass
    
Instances are created by calling the constructor of the class. These constitute the nodes of the graph.
In [2]:
    
a1_001 = A1()
a1_001
    
    Out[2]:
Instances typically have scalar attributes that can be set during construction. For example, all instances have an id attribute.
In [3]:
    
a1_001 = A1(id='instance_001_of_A1')
a1_001.id
    
    Out[3]:
Instances can be type-checked against any parent class.
In [4]:
    
isinstance(a1_001,A1) and isinstance(a1_001,A) and isinstance(a1_001,Molecule)
    
    Out[4]:
Instances have relations to each other, which constitute the edges of the graph. Relations are managed using pairs of attributes on each of the respective classes. For example, Molecule has a sites attribute that holds a list of site instances. Site has a molecule attribute that holds a single instance of a molecule.
In [5]:
    
m,s = Molecule(), Site()
m.sites, s.molecule
    
    Out[5]:
In [6]:
    
m.sites.append(s)
m.sites
    
    Out[6]:
In [7]:
    
s.molecule
    
    Out[7]:
The convention for attribute name is typically to use a singular name such as molecule if it refers to a single instance or a plural name such as sites if it refers to a list of instances.
wc_rules provides setters, getters and unsetters for various instance attributes. For example, for the id attribute, we have get_id() and set_id(id).
In [8]:
    
m = Molecule()
m.set_id('m_001')
m.get_id()
    
    Out[8]:
Typically, the following naming conventions are followed for getters, setters and unsetters:
| Attribute type | Getter | Setter | Unsetter | 
|---|---|---|---|
| Scalar | get_* | 
set_* | 
|
| Object reference | get_* | 
set_* | 
unset_* | 
| List of object references | get_* | 
add_* | 
remove_* | 
Setters and unsetters always "return self", so they can be chained indefinitely. For example,
In [9]:
    
m = Molecule().set_id('m_001').add_sites( Site(), Site() )
m.sites
    
    Out[9]:
Additional points to remember about instance attribute methods:
Molecule.add_sites() and Site.set_molecule() will have the same effect of establishing relations between molecules and sites.Class attributes dictate behavior of a class and can be modified during subclassing. For example, the Site method has the following class attributes:
allowed_to_bind, a Boolean that indicates whether a bond is allowed on that site type,allowed_molecule_types, a tuple of Molecule classes that are allowed to be related to that site. Below, we
Molecule subclasses A and B, Site subclasses X1, X2, Y and Z, 
In [10]:
    
class A(Molecule):pass
class B(Molecule):pass
class X1(Site):
    allowed_site_types = (A,)
class X2(Site):
    allowed_site_types = (A,)
class Y(Site):
    allowed_site_types = (B,)
class Z(Site):
    allowed_site_types = (B,)
    allowed_to_bind = False