1

Is there anyone who could help figure out what the double greater-than symbol (i.e. Drug >> float in line 10 and Drug >> int in line 13) means in the following python scripts?

 1  from owlready2 import *
 2
 3  onto = get_ontology("http://test.org/onto.owl")
 4
 5  with onto:
 6      class Drug(Thing):
 7          def get_per_tablet_cost(self):
 8              return self.cost / self.number_of_tablets
 9
10      class has_for_cost(Drug >> float, FunctionalProperty):
11          python_name = "cost"
12
13      class has_for_number_of_tablets(Drug >> int, FunctionalProperty):
14          python_name = "number_of_tablets"
15
16  my_drug = Drug(cost = 10.0, number_of_tablets = 5)
17  print(my_drug.get_per_tablet_cost())

--- Below is the printing outcome. ---
2.0

The code is copied from here and runs in python 3.

The >> symbol here is not bitwise operator. Python's bitwise operation, say x >> y, requires y to be an integer. It is clear that the code here (Drug >> float) is not shifting Drug right by float bits.

Although similar questions were asked before (see here and here), their answers mostly pointed to the so-called "print chevron" in python 2, where the >> redirects the to-be-printed messages to the file-like object specified right after the >>. They do not apply to my question because here is not a case of print statement.

To trace the >> in the code above, I revised the script in line 10 as class has_for_cost(Drug >> list, FunctionalProperty) (namely replace >> float with >> list) and ran the code again. It raised an AttributeError as follows:

AttributeError                            Traceback (most recent call last)
<ipython-input-1-1be374b27b04> in <module>()
      8             return self.cost / self.number_of_tablets
      9 
---> 10     class has_for_cost(Drug >> list, FunctionalProperty):
     11         python_name = "cost"
     12 

~/anaconda3/lib/python3.6/site-packages/owlready2/prop.py in __init__(Prop, name, bases, obj_dict)
    254 class ReasoningPropertyClass(PropertyClass):
    255   def __init__(Prop, name, bases, obj_dict):
--> 256     super().__init__(name, bases, obj_dict)
    257 
    258     if (not Prop.namespace.world is owl_world):

~/anaconda3/lib/python3.6/site-packages/owlready2/prop.py in __init__(Prop, name, bases, obj_dict)
     78 
     79       if not range is False:
---> 80         Prop.range.extend(range)
     81 
     82       if not inverse_property is False:

~/anaconda3/lib/python3.6/site-packages/owlready2/util.py in extend(self, l)
     58   def append(self, x):          old = list(self); super().append(x)         ; self._callback(self._obj, old)
     59   def insert(self, i, x):       old = list(self); super().insert(i, x)      ; self._callback(self._obj, old)
---> 60   def extend(self, l):          old = list(self); super().extend(l)         ; self._callback(self._obj, old)
     61   def remove(self, x):          old = list(self); super().remove(x)         ; self._callback(self._obj, old)
     62   def __delitem__(self, i):     old = list(self); super().__delitem__(i)    ; self._callback(self._obj, old)

~/anaconda3/lib/python3.6/site-packages/owlready2/prop.py in _range_changed(Prop, old)
    143     for x in new - old:
    144       if isinstance(x, ClassConstruct): x._set_ontology(Prop.namespace.ontology)
--> 145       x2 = _universal_datatype_2_abbrev.get(x) or x.storid
    146       Prop.namespace.ontology.add_triple(Prop.storid, rdf_range, x2)
    147 

AttributeError: type object 'list' has no attribute 'storid'

With the traceback info, though, I still could not find the answer. I would be very thankful if anyone could help figure out what the >> does in the code.

go4it
  • 33
  • 4
  • 1
    Classes can implement the bitshift operator however they choose, some less intuitively than others. The magic method you should be looking for in the class definition is `__rshift__(self, other)`, `__rrshift__(self, other)`, or `__irshift__(self, other)` – jedwards Jul 04 '18 at 22:22
  • @jedwards is right. It is `__rshift__()` defined in a base object, `ThingClass`, do the trick. Details can be found [here](https://stackoverflow.com/a/51184773/10033543). – go4it Jul 05 '18 at 06:47

2 Answers2

3

Drug >> float calls Drug's __rshift__ method. That method can do whatever it wants; it doesn't have to be a binary shift operation.

Reading the owlready2 documentation reveals that domain >> range is a fancy way of creating an ObjectProperty.

Aran-Fey
  • 39,665
  • 11
  • 104
  • 149
2

Aran-Fey has answered the question in brief. Below is just to save time for those who don't want to download and parse the source codes of owlready2 but still want to trace how it works in the actual codes.

  1. Drug is an object created on the basis of another object ThingClass, as defined in /owlready2/entity.py.
  2. Drug >> float calls Drug.__rshift__(self, float) [explained].
  3. It is not Drug but its base object ThingClass that has a __rshift__ method, which reads in the entity.py:

    class ThingClass(EntityClass):
        ...
        def __rshift__(Domain, Range):
            import owlready2.prop
            owlready2.prop._next_domain_range = (Domain, Range)
            if isinstance(Range, ThingClass) or isinstance(Range, ClassConstruct):
                return owlready2.prop.ObjectProperty
            else:
                return owlready2.prop.DataProperty
    
  4. Drug >> float is equivalent to __rshift__(Domain=Drug, Range=float), which is in fact not doing a binary arithmetic operation but returns/creates either a DataProperty or an ObjectProperty, depending on the type of Range.

go4it
  • 33
  • 4