Calling from the same class, why is one treated as bound method while the other plain function?
Question: I have the following code snippet in Python 3:

    
    
    from sqlalchemy.ext.declarative import declared_attr
    from sqlalchemy import Column, Integer, String, Unicode, UnicodeText
    from sqlalchemy.ext.hybrid import hybrid_property, hybrid_method
    import arrow
    
    
    datetimeString_format = {
        "UTC": "%Y-%m-%d %H:%M:%S+00:00",
        "local_with_timezoneMarker": "%Y-%m-%d %H:%M:%S %Z",
        "local_without_timezoneMarker": "%Y-%m-%d %H:%M:%S"
    }
    
    dateString_format = "%Y-%m-%d"
    
    
    
    
    class My_TimePoint_Mixin:
        # define output formats:
        datetimeString_inUTC_format = "%Y-%m-%d %H:%M:%S+00:00"
        datetimeString_naive_format = "%Y-%m-%d %H:%M:%S"
    
        # instrumented fields:
        _TimePoint_in_database = Column('timepoint', String, nullable=False)
        _TimePoint_in_database_suffix = Column(
        'timepoint_suffix', String, nullable=False)
    
        @hybrid_property
        def timepoint(self):
            twoPossibleType_handlers = [
                self._report_ACCRT_DATE,
                self._report_ACCRT_DATETIME
            ]
            for handler in twoPossibleType_handlers:
                print("handler: ", handler)
                try:
                    return handler(self)
                except (AssertionError, ValueError) as e:
                    logging.warning("Try next handler!")
    
        @timepoint.setter
        def timepoint(self, datetimepointOBJ):
            handlers_lookup = {
                datetime.datetime: self._set_ACCRT_DATETIME,
                datetime.date: self._set_ACCRT_DATE
            }
            this_time = type(datetimepointOBJ)
            this_handler = handlers_lookup[this_time]
            print("handler: ", this_handler)
            this_handler(datetimepointOBJ)
    
        def _report_ACCRT_DATE(self):
            """Accurate Date"""
            assert self._TimePoint_in_database_suffix == "ACCRT_DATE"
            date_string = self._TimePoint_in_database
            dateString_format = "%Y-%m-%d"
            # return a datetime.date
            return datetime.datetime.strptime(date_string, dateString_format).date()
    
        def _report_ACCRT_DATETIME(self):
            """Accurate DateTime"""
            assert self._TimePoint_in_database_suffix in pytz.all_timezones_set
            datetimeString_inUTC = self._TimePoint_in_database
            utc_naive = datetime.datetime.strptime(
            datetimeString_inUTC, self.datetimeString_inUTC_format)
            utc_timepoint = arrow.get(utc_naive, "utc")
            # localize
            local_timepoint = utc_timepoint.to(self._TimePoint_in_database_suffix)
            # return a datetime.datetime
            return local_timepoint.datetime
    
        def _set_ACCRT_DATETIME(self, datetimeOBJ_aware):
            assert isinstance(datetimeOBJ_aware, datetime.datetime), "Must be a valid datetime.datetime!"
            assert datetimeOBJ_aware.tzinfo is not None, "Must contain tzinfo!"
            utctime_aware_arrow = arrow.get(datetimeOBJ_aware).to('utc')
            utctime_aware_datetime = utctime_aware_arrow.datetime
            store_datetime_string = utctime_aware_datetime.strftime(
            self.datetimeString_inUTC_format)
            self._TimePoint_in_database = store_datetime_string
    
        def _set_ACCRT_DATE(self, dateOBJ):
            store_date_string = dateOBJ.isoformat()
            self._TimePoint_in_database = store_date_string
    

For some reason, the getter's handler is treated as a plain function rather
than a method, hence the need to explicitly provide 'self' as its argument.

Is it because of the looping? Or because of the `try...except` structure? Why
is this the case that within the same class, handlers are treated differently?
(The setter's handlers are treated as bound method as expected).


Answer: What you have is not a regular `property` here, you have a [SQLAlchemy
`@hybrid_property`
object](http://docs.sqlalchemy.org/en/latest/orm/extensions/hybrid.html).
Quoting the documentation there:

> “hybrid” means the attribute has distinct behaviors defined at the class
> level and at the instance level.

and

> When dealing with the `Interval` class itself, the `hybrid_property`
> descriptor evaluates the function body given the `Interval` class as the
> argument, which when evaluated with SQLAlchemy expression mechanics returns
> a new SQL expression:
>  
>  
>     >>> print Interval.length
>     interval."end" - interval.start
>  

So the property is used in a _dual capacity_ , both on instances, and on the
class.

In the case of the property being used on the class itself, `self` is bound to
(a subclass of) `My_TimePoint_Mixin` and the methods are not bound. There is
nothing to bind _to_ in that case as there is no instance.

You'll have to take this into account when coding a `hybrid_property` getter
(the setter only applies to the _on an instance_ case). Your assertions at the
start of `_report_ACCRT_DATE` and `_report_ACCRT_DATETIME` won't hold, for
example.

You can distinguish between the _instance_ case and the _expression_ (on the
class) case, by declaring a separate getter for the latter with the
`hybrid_property.expression` decorator:

    
    
    @hybrid_property
    def timepoint(self):
        twoPossibleType_handlers = [
            self._report_ACCRT_DATE,
            self._report_ACCRT_DATETIME
        ]
        for handler in twoPossibleType_handlers:
            print("handler: ", handler)
            try:
                return handler(self)
            except (AssertionError, ValueError) as e:
                logging.warning("Try next handler!")
    
    @timepoint.expression
    def timepoint(cls):
        # return a SQLAlchemy expression for this virtual column
    

SQLAlchemy will then use the `@timepoint.expression` class method for the
`My_TimePoint_Mixin.timepoint` use, and use the original getter only on
`My_TimePoint_Mixin().timepoint` instance access. See the [_Defining
Expression Behavior Distinct from Attribute Behavior_
section](http://docs.sqlalchemy.org/en/latest/orm/extensions/hybrid.html#defining-
expression-behavior-distinct-from-attribute-behavior).

