internal circular class references

Ethan Furman ethan at stoneleaf.us
Thu Dec 11 18:33:14 CET 2008


Carl Banks wrote:
> 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
<snip>
>>   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()

Well, since "Practicality beats purity" ;) , I'll stick with having the 
two assignment statements just outside the class.  Not to mention my 
metaclass skills are nonexistent at this point.

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

Critiques always welcome.

> 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

Good question.  My goal with NullDate is to have a date object that I 
can treat the same regardless of whether or not it actually holds a 
date.  NullDates with no value should sort before any NullDates with a 
value, should be comparable to dates as well as NullDates, and should 
support all the same methods.  In other words, I don't want to have to 
worry about whether my date object has an actual date under most 
circumstances (printing, using as dictionary keys, comparing, etc.).

Does my design make more sense given these expanded requirements, or 
could it still be done simpler?  For that matter, do my requirements 
make sense?

~Ethan~



More information about the Python-list mailing list