Some more food for though. To simply obtain what you want to do in your first set of code, you can do that without all the extra tricks.
The simplest way to do it is set the attributes to the desired one directly. (code consolidated in improper manors simply to save space)
class Note:
def __init__(self, name, value): self.name, self._size = name, value
size = property(lambda x: x._size+1)
class Notebook(object):
def __new__(cls, *notes):
notebook = object.__new__(cls)
notebook._notes = {note.name: note.size for note in notes}
for note in notes: setattr(notebook, note.name, note.size)
return notebook
memo1, report1 = Note("memo", 5), Note("report", 20)
notebook1 = Notebook(memo1, report1)
print(notebook1.memo, notebook1.report) # 6 21
memo2, report2 = Note("memo", 35), Note("report", 40)
notebook2 = Notebook(memo2,report2)
print(notebook2.memo, notebook2.report) # 36 41
print(notebook1.memo, notebook1.report) # 6 21
notebook1.memo += 5
print(notebook1.memo) # 11
print(memo1.size) # 6
memo1.size += 5 # AttributeError: can't set attribute
The second way would be to have the notebook literally be a container for all the notes you pass to it. This way it would simply update the original class objects, and is basically just a holder for them.
class Note2(object):
def __init__(self, name, value): self.name, self._size = name, value
def _set_size(self, value): self._size = value
size = property(lambda x: x._size+1, _set_size)
def __repr__(self): return str(self.size) #simple trick to gain visual access to .size
class Notebook2(object):
def __new__(cls, *notes):
notebook = object.__new__(cls)
notebook._notes = {note.name: note.size for note in notes}
for note in notes: setattr(notebook, note.name, note)
return notebook
memo1, report1 = Note2("memo", 5), Note2("report", 20)
notebook1 = Notebook2(memo1, report1)
print(notebook1.memo, notebook1.report) # 6 21
memo2, report2 = Note2("memo", 35), Note2("report", 40)
notebook2 = Notebook2(memo2, report2)
print( notebook2.memo, notebook2.report) # 36 41
print(notebook1.memo, notebook1.report) # 6 21
notebook1.memo.size += 16
print(notebook1.memo) # 23
print(memo1) # 23, Notice this will also set the original objects value to the new value as well
notebook1.memo += 15 # TypeError: unsupported operand type(s) for +=: 'Note2' and 'int' - It is true without making it as a property does make it less effective to work with
It should also be possible to do as in your provided link suggests to make each Note class a member of Notebook with a leading underscore (i.e. notebook._memo) and then make a property for Notebook which would link Note name to size (i.e. notebook.memo would be a link to notebook._memo.size). Hope these examples help.
Original answer.
Interesting idea, to simply get it working here is a hack of your original version:
class Note(object):
def __init__(self,name, size):
self.name = name
self._size = size
def _get_size(self, notebook_class=None):
return self._size
def _set_size(self, notebook_class=None, size=0):
self._size = size
class Notebook(object):
def __new__(cls,*notes):
notebook = object.__new__(cls)
for note in notes:
setattr(notebook.__class__, note.name, property(note._get_size, note._set_size))
return notebook
However you seem to be removing each Note class when you ingest them into Notebook anyways so you could do something much easier:
class Note(object):
def __init__(self, name, size):
self.name = name
self.size = size
class Notebook(object):
def __new__(cls, *notes):
notebook = object.__new__(cls)
for note in notes:
setattr(notebook.__class__, note.name, note.size)
return notebook
To be any more helpful I would really need to know the goal or a general idea of where you want to take this. It seems confusing to set the properties in such an odd way, yet only do it once at the creation of the class as opposed to the examples of being able to dynamical add and remove them.
Hope this helped