internal circular class references

Carl Banks pavlovevidence at gmail.com
Wed Dec 10 21:55:59 EST 2008


On Dec 10, 5:26 pm, Ethan Furman <et... at stoneleaf.us> wrote:
> Greetings List!
>
> I'm writing a wrapper to the datetime.date module to support having no
> date.  Its intended use is to hold a date value from a dbf file, which
> can be empty.
>
> The class is functional at this point, but there is one thing I would
> like to change -- datetime.date.max and datetime.date.min are class
> attributes of datetime.date, and hold datetime.date values.  At this
> point I have to have two lines outside the actual class definition to do
> the same thing, e.g.:
>
> <trimmed down class code>
>    class NullDate(object):
>        "adds null capable DateTime.Date constructs"
>        __slots__ = ['_date']
>        def __new__(cls, date='', month=0, day=0):
>            nulldate = object.__new__(cls)
>            nulldate._date = ""
>                 .
>                 .
>                 .
>           return nulldate
>        def __getattr__(self, name):
>            if self:
>                attribute = self._date.__getattribute__(name)
>                return attribute
>            else:
>                if callable(dt.date.__dict__[name]):
>                    return int
>                else:
>                    return 0
>        def __nonzero__(self):
>            if self._date:
>                return True
>            return False
>        @classmethod
>        def fromordinal(cls, number):
>            if number:
>                return cls(dt.date.fromordinal(number))
>            else:
>                return cls()
>    NullDate.max = NullDate(dt.date.max)
>    NullDate.min = NullDate(dt.date.min)
> </trimmed down class code>
>
> How can I move those last two lines into the class definition so that:
>    1) they are class attributes (not instance), and
>    2) they are NullDate type objects?


It can't be done by any straightforward method I know of.  I advise
you not to worry about it, and just define them afterwards, perhaps
with an apologetic comment saying you would have put them inside the
class definition if you could have.


If the out-of-scope issue bothers you that much, you could use some
metaclass hackery to run a method that defines the class attributes
after the class is created, at the unrecommendable cost of confusing
most readers.  Here is a very simple example:

def make_class_and_run_postcreate(name,bases,clsdict):
    cls = type.__new__(type,name,bases,clsdict)
    cls.__postcreate__()
    return cls

class A(object):
    __metaclass__ = make_class_and_run_postcreate
    @classmethod
    def __postcreate__(cls):
        cls.internal_circular_class_ref = cls()


BTW, if you don't mind some criticism of your code, the code you
posted seems to be much too complex for the job you're describing.

First of all, do you even need to wrap the datetime.date class?  With
Python's duck typing ability, you could have a separate NullDate class
to go alongside the datetime.date, and use a regular datetime.date
object when the date is present, and NullDate when it's absent.  If
necessary you can subclass datetime.date to add any new methods it
would have to have.  Use a factory function to return either NullDate
or a datetime.date depending on whether the dbf cell is empty.

class ValidDate(datetime.date):
    def is_valid(self):
        return True

class NullDate(object):
    # implement any necessary methods of datetime.date interface here
    def is_valid(self):
        return False

def create_date_from_dbf_cell(dbf_cell):
    if dbf_cell.empty():
        return NullDate()
    return ValidDate(dbf_cell.value)


If you do this, you don't have to muck around with __getattr__ or
__new__ or snooping to datetime.date's class dict anything like that.



Carl Banks



More information about the Python-list mailing list