On Sat, 23 Jul 2005 10:59:56 -0400, "Jeffrey E. Forcier" <[EMAIL PROTECTED]> wrote:
>Thanks for all the additional replies thus far! > >Apparently the issue, as stated implicitly or explicitly by most of >you, is that new-style class instances essentially defer their magic >methods to the class's static versions of same. This is good to know :) Actually, it's not just the "magic" methods. If you have an instance a of a newstyle class A, any attribute lookup a.attr will undergo the same search first to see if attr is a descriptor object, and if not, *then* to look in the instance attribute directory. But the descriptor search doesn't start in inst.__dict__, it goes through the chain of classes and base classes provided by type(inst).mro(), which starts in type(inst). And for our class A instance a, type(a) will be A, so the search for a.attr starts there. Same applies to a.__str__. This ensures that all instances of the same class will share the same methods. The way a method, which is just a class variable with a function as its value, gets to be a callable bound method, is the same as any attribute lookup looking for a descriptor with a __get__ method (which a function also has, for this purpose). If the descriptor doesn't have a __set__ method as well, then an instance attribute takes priority. If there is a __set__ method, and instance attribute can't shadow the attribute name, and the descriptor __get__ method takes precedence. Unshadowed, a method search looks something like cbm = ((base for base in type(inst).mro() if 'attr' in base.__dict__) .next().__dict__['attr'].__get__(inst, type(inst))) if this doesn't succeed and meet the __set__ vs shadowing logic, then you get the instance attribute per se. Jumping ahead using a MyClass inst as an example: >>> cbm = ((base for base in type(inst).mro() if '__str__' in base.__dict__) ... .next().__dict__['__str__'].__get__(inst, type(inst))) >>> cbm <bound method MyClass.View of I, <__main__.MyClass object at 0x02FB91AC>, am being viewed> This looks a little strange because the repr of the bound method includes a repr of the thing bound to, which returns the Edit/View presentation (after the 'of' above). (A possible reason to consider using just __str__ and letting __repr__ be). A function's __get__ method will deliver a bound method if the lookup is via an inst, or an unbound method if the lookup is via the class, in which case None is passed to __get__ instead of the instance. The upshot is that you can create descriptors __str__ and __repr__for your class that will return bound methods using __str__ and __repr__ function attributes of your instance (if they exist) instead of the normal class attributes, and we can chose from several normal class attributes according to a general mode flag of the class. E.g., using your original example, and rearranging things a bit, we can do something like what (I think) you wanted: (The first class below defines descriptors and the example uses two instances in MyClass) >>> class InstanceMethodSetterAndBinder(object): ... def __init__(self, inst_attr_name): ... # lambda self:'?? no %s format ??'% object.__repr__(self) ... self.inst_attr_name = inst_attr_name ... def __get__(self, inst, cls=None): ... if inst is None: return self ... default = getattr((cls or type(inst)), 'SharedEVMethod', None) ... if default is None: ... def default(self): ... return '<?? no SharedEVMethod for %s ??>'% object.__repr__(inst) ... return vars(inst).get(self.inst_attr_name, default).__get__(inst, cls) ... def __set__(self, inst, value): ... inst.__dict__[self.inst_attr_name] = value ... def __delete__(self, inst): ... try: del inst.__dict__[self.inst_attr_name] ... except KeyError: pass ... >>> class MyClass(object): ... __str__ = InstanceMethodSetterAndBinder('__str__') ... __repr__ = InstanceMethodSetterAndBinder('__repr__') ... @staticmethod ... def Edit(self): ... return "I, %s, am being edited" % (object.__repr__(self)) # %s on self recurses!! ... @staticmethod ... def View(self): ... return "I, %s, am being viewed" % (object.__repr__(self)) # %s on self recurses!! ... SharedEVMethod = View ... def setEdit(self, shared=True): ... if not shared: ... self.__str__ = self.__repr__ = self.Edit ... else: ... type(self).SharedEVMethod = self.Edit ... def setView(self, shared=True): ... if not shared: ... self.__str__ = self.__repr__ = self.View ... else: ... type(self).SharedEVMethod = self.View ... def delInstEVM(self): ... del self.__str__ ... del self.__repr__ ... >>> inst = MyClass() >>> inst2 = MyClass() >>> inst3 = MyClass() >>> inst I, <__main__.MyClass object at 0x02FB91AC>, am being viewed >>> inst2 I, <__main__.MyClass object at 0x02FB91CC>, am being viewed >>> inst3 I, <__main__.MyClass object at 0x02FB91EC>, am being viewed >>> inst.setEdit() >>> inst I, <__main__.MyClass object at 0x02FB91AC>, am being edited >>> inst2 I, <__main__.MyClass object at 0x02FB91CC>, am being edited >>> inst3 I, <__main__.MyClass object at 0x02FB91EC>, am being edited >>> inst2.setView(False) >>> inst I, <__main__.MyClass object at 0x02FB91AC>, am being edited >>> inst2 I, <__main__.MyClass object at 0x02FB91CC>, am being viewed >>> inst3 I, <__main__.MyClass object at 0x02FB91EC>, am being edited >>> inst2.setView() # all >>> inst2.setEdit(False) # just inst >>> inst I, <__main__.MyClass object at 0x02FB91AC>, am being viewed >>> inst2 I, <__main__.MyClass object at 0x02FB91CC>, am being edited >>> inst3 I, <__main__.MyClass object at 0x02FB91EC>, am being viewed >>> inst2.delInstEVM() >>> inst2 I, <__main__.MyClass object at 0x02FB91CC>, am being viewed You could use this kind of thing in a base class and specialize in subclasses to override stuff, and you can do other stuff too. Your choices are more varied that you probably thought ;-) > >Ironically, the reason I'm using new-style classes is that I only >read up on them very recently, and was attempting to get myself into >the habit of inheriting from 'object' by default. Go figure. > >Anyway, to take a step back, the *actual* actual reason for my >investigation into this issue is that I'm attempting to create a >collection of classes representing HTML page/form elements, many of >which are represented differently depending on whether they're in a >"view" or "edit" state. > >And ideally I wanted to be able to hold a collection of these objects >and toggle them all to one state or the other, then bandy them about ^^^^^^^^^^^^^^^ does that mean all instances with a single toggling action, or each individually? You could have both. I.e., a general mode flag and the ability to assign an arbitrary __str__ and/or __repr__ override for a particular instance. See example, where all are toggled by default. >as if they were strings, e.g. mixing them with literal strings via str >(obj). Note that your example set __repr__ also, which is not used for str(x) if x.__str__ exists. > >Clearly there are at least a handful of ways to accomplish this, but >the one that came to mind first was, as I said at the beginning, to >define both behaviors on each object and then have the ability to >point __str__ to one or the other. I suppose now I need to figure out >which is more important, that ease-of-use of overriding __str__ or >whatever benefits new-style classes give (something I'm still a bit >unclear on). > Those aren't your only choices ;-) Regards, Bengt Richter -- http://mail.python.org/mailman/listinfo/python-list