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

<pre><code>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
</code></pre>

<p>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.</p>

<p>Is it because of the looping? Or because of the <code>try...except</code> 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).</p>

<p>What you have is not a regular <code>property</code> here, you have a <a href="http://docs.sqlalchemy.org/en/latest/orm/extensions/hybrid.html" rel="nofollow">SQLAlchemy <code>@hybrid_property</code> object</a>. Quoting the documentation there:</p>

<blockquote>
  <p>âhybridâ means the attribute has distinct behaviors defined at the class level and at the instance level.</p>
</blockquote>

<p>and</p>

<blockquote>
  <p>When dealing with the <code>Interval</code> class itself, the <code>hybrid_property</code> descriptor evaluates the function body given the <code>Interval</code> class as the argument, which when evaluated with SQLAlchemy expression mechanics returns a new SQL expression:</p>

<pre><code>&gt;&gt;&gt; print Interval.length
interval."end" - interval.start
</code></pre>
</blockquote>

<p>So the property is used in a <em>dual capacity</em>, both on instances, and on the class.</p>

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

<p>You'll have to take this into account when coding a <code>hybrid_property</code> getter (the setter only applies to the <em>on an instance</em> case). Your assertions at the start of <code>_report_ACCRT_DATE</code> and <code>_report_ACCRT_DATETIME</code> won't hold, for example.</p>

<p>You can distinguish between the <em>instance</em> case and the <em>expression</em> (on the class) case, by declaring a separate getter for the latter with the <code>hybrid_property.expression</code> decorator:</p>

<pre><code>@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
</code></pre>

<p>SQLAlchemy will then use the <code>@timepoint.expression</code> class method for the <code>My_TimePoint_Mixin.timepoint</code> use, and use the original getter only on <code>My_TimePoint_Mixin().timepoint</code> instance access. See the <a href="http://docs.sqlalchemy.org/en/latest/orm/extensions/hybrid.html#defining-expression-behavior-distinct-from-attribute-behavior" rel="nofollow"><em>Defining Expression Behavior Distinct from Attribute Behavior</em> section</a>.</p>

python function python-3.x methods sqlalchemy