Albert-Jan Roskam wrote: > Why does following the line (in #3)
> # 3------------------------------------------------- > class Meta(type): > def __new__(cls, name, bases, attrs): > for attr, obj in attrs.items(): > if attr.startswith('_'): > continue > elif not isinstance(obj, property): > import pdb;pdb.set_trace() > #setattr(cls, attr, property(lambda self: obj)) # > #incorrect! > raise ValueError("Only properties allowed") > return super().__new__(cls, name, bases, attrs) > > class MyReadOnlyConst(metaclass=Meta): > __metaclass__ = Meta > YES = property(lambda self: 1) > NO = property(lambda self: 0) > DUNNO = property(lambda self: 42) > THROWS_ERROR = 666 > > > c2 = MyReadOnlyConst() > print(c2.THROWS_ERROR) > #c2.THROWS_ERROR = 777 > #print(c2.THROWS_ERROR) > not convert the normal attribute into > a property? > > setattr(cls, attr, property(lambda self: obj)) # incorrect! cls is Meta itself, not MyReadOnlyConst (which is an instance of Meta). When the code in Meta.__new__() executes MyReadOnlyConst does not yet exist, but future attributes are already there, in the form of the attrs dict. Thus to convert the integer value into a read-only property you can manipulate that dict (or the return value of super().__new__()): class Meta(type): def __new__(cls, name, bases, attrs): for attr, obj in attrs.items(): if attr.startswith('_'): continue elif not isinstance(obj, property): attrs[attr] = property(lambda self, obj=obj: obj) return super().__new__(cls, name, bases, attrs) class MyReadOnlyConst(metaclass=Meta): YES = property(lambda self: 1) NO = property(lambda self: 0) DUNNO = property(lambda self: 42) THROWS_ERROR = 666 c = MyReadOnlyConst() try: c.THROWS_ERROR = 42 except AttributeError: pass else: assert False assert c.THROWS_ERROR == 666 PS: If you don't remember why the obj=obj is necessary: Python uses late binding; without that trick all lambda functions would return the value bound to the obj name when the for loop has completed. A simplified example: >>> fs = [lambda: x for x in "abc"] >>> fs[0](), fs[1](), fs[2]() ('c', 'c', 'c') >>> fs = [lambda x=x: x for x in "abc"] >>> fs[0](), fs[1](), fs[2]() ('a', 'b', 'c') _______________________________________________ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor