-3

My code dynamically generates properties and the classes to contain them. The code creates immutably-typed properties such that assignments to them are automatically converted to the correct type, rather than be rejected.

The problem is that, for property c.p the getter (t=c.p) and setter (c.p=t) are not being invoked. Code can explicitly invoke the getter with t=w.p.__get__(0) . Code can explicitly invoke the setter with c.p.__set__(3,0) . However, that ruins the convenience of having properties in the first place.

Symptoms:

for property p in class c:

t=c.p does not invoke the getter. Instead the assignment causes t to then refer to the property object itself, rather than the property's value.

t=c.p.__get__() fails with TypeError: expected at least 1 argument, got 0

t=c.p.__get__(None) fails with TypeError: get(None, None) is invalid

t=c.p.__get__(0) invokes the getter. t gets the value returned by the getter. The value of the parameter does not matter, as long as it is not None.

c.p=3 does not invoke the setter. Instead, c.p is now an integer with value 3.

After reconstructing the property:

c.p.__set__(3) fails with TypeError: expected 2 arguments, got 1

c.p.__set__(3,0) fails with TypeError: fImmutablyTypedPropertiesContainer..cImmutablyTypedPropertiesContainer.addProperty..psetter() takes from 0 to 1 positional arguments but 2 were given

Altering the setter's signature to accept a optional second parameter then allows c.p.__set__(3,0) or even c.p.__set__(3,None) to work.

c.p.__set__(3,0) # now works

What must be done to get the property's typical usage to invoke the getter and setter as intended?

code:

#!/bin/python3.13
# immutably typed properties (the type survives setting a new value)

import numpy as np


def fImmutablyTypedPropertiesContainer():
    # Purpose: Create a Class to be a container to which staticly typed properties can be added, then can get and set.
    # Each property's type is immutable, although its value may change. An np.longdouble stays an np.longdouble.
    # Each call creates and returns a new class, NOT an instance. No instances are created.
    # That way multiple invocations of this function each get their own container and namespace.
    # The static typing survives a set to a new value, even if conversion & coercion at assignment time is required.
    # Different properties within the container need not all be of the same type.
    #
    # Usage:
    #   bucket= fImmutablyTypedPropertiesContainer()   # create a container
    #   bucket.addProperty('i',75.6,ptype=np.int64)     # add a property i with value stored in _i[0] set to 75
    #   bucket.addProperty('x',144,ptype=np.longdouble) # add a property x with value stored in _x[0] set to 144.0
    #   bucket.i                                       # get i's value
    #   bucket.x = 44                                  # set x's value to 44.0
    #
    # import numpy as np # assume already imported
    
    # create the class   

    class cImmutablyTypedPropertiesContainer(object):
  
       pass # no instances

       @classmethod
       def addProperty(cls,name,value=np.longdouble(2.71828182845),ptype=np.longdouble) : #cls is the class, not an instance. 
           #               name is a string, such as "i" 
           
           setattr(cls, "_"+name, np.ndarray((1,),dtype=ptype) ) # create an array containing a single item as an attribute called _name
                                  # elements of a numpy array have a static type, with conversion & coercion at assignment time
           getattr(cls, "_"+name)[0]=value # this SETs the initial value of the only element of the _name array, including automatic type conversion.
           
           print("adding property named: ", name ," to class: ", cls," at", id(cls), ". with parm value:", value, " in ", "_"+name, "with value ",getattr(cls, "_"+name)," \n" )
 
           #MUST NOT BE @classmethod.  Causes TypeError: 'classmethod' object is not callable
           def pgetter(a1):#*args,**kwargs): # arguments are unused
               t=pgetter.fromwhattoget[0]  # set externally
               print("invoked the getter for name= ",name, pgetter.fromwhattoget, "; value= ",t, ". a1=",a1)
               # elements of a numpy array have a static type, with conversion & coercion at assignment time
               return t
           pgetter.fromwhattoget=getattr(cls, "_"+name)
           
           #MUST NOT BE @classmethod. Causes TypeError: 'classmethod' object is not callable
           def psetter(newvalue=3.14159, dummyparm=0): #must allow for two parameters
               print("invoked a setter for name ",name, psetter.intowhattoset, " = ",newvalue)
               # elements of a numpy array have a static type, with conversion & coercion at assignment time
               psetter.intowhattoset[0]=newvalue # this SETs a value of the first element of the _name array, including automatic type conversion.
               return psetter.intowhattoset[0] # getattr(cls, "_"+name)[0]   # also returns the value, but the __get__ function does not pass it back.
           psetter.intowhattoset=getattr(cls, "_"+name)
           
           print("fromwhat to get and into what toset = ",pgetter.fromwhattoget, psetter.intowhattoset)          
           setattr(cls,name, property( fget=pgetter, fset=psetter) )
           return
          
    return cImmutablyTypedPropertiesContainer # return the class itself, not an instance

v=fImmutablyTypedPropertiesContainer() # v is a unique class itself, rather than an instance.  
v.addProperty('i',1024,ptype=np.int64)

w=fImmutablyTypedPropertiesContainer() # w is a unique class itself, rather than an instance.  
w.addProperty('i',75.6,ptype=np.int64)
print("added property named: w.i", w.i, w.i.__get__(0), type(w.i)," to class: ",w," with parm value:  75.6, ", "but actual value: ", w.i )

print("\n getting:")
t=w.i
tg=w.i.__get__
tv=w.i.__get__(10) # must have a parameter, even though the getter does not use it. Cannot be None.
print("t=w.i= ",t,", getter:", tg, ", value:", tv)

print("\n setting:")
ts=w.i.__set__
w.i.__set__(314,None) #must have two parameters, even though the setter does not use the second.
tv=w.i.__get__(w.i)
print("t=w.i= ",t,", setter:", ts,", value:", tv)
tvr=w.i.__set__(342,0) #must have two parameters, even though the setter does not use the second.
tv=w.i.__get__(w.i)
print("t=w.i= ",t,", setter:", ts,", value:", tv, tvr) # tvr is None. __set__ does not return a value.

t=w.i
print("t=w.i= ",t,", w.i=",w.i,", with type=",type(w.i))
print("\n")
print("w.i= ",w.i.__get__(0), type(w.i), w.i,w, type(w), id(w))
print("v.i= ",v.i.__get__(0), type(v.i), v.i,v, type(v), id(v))

w.i=3
t=w.i
print("t=w.i= ",t,", w.i=",w.i,", with type=",type(w.i))
print("\n")
print("w.i= ",w.i.__get__(0), type(w.i), w.i,w, type(w), id(w))


quit()

references:

5
  • 1
    Please try to pare this down to a more readable minimal reproducible example, and if you're going to include links, include them as links rather than code. Commented Feb 17 at 20:09
  • 2
    Also, are you 100% sure your question isn't answered (in whole or in part) by any of your 20+ references? Commented Feb 17 at 20:15
  • The second link fails for me. I don't think you can view other users' saved questions. And if you want to refer to a specific answer, link directly to it by using the share button. "top answer" is meaningless because order is dynamic. Commented Feb 17 at 21:21
  • Your function fImmutablyTypedPropertiesContainer ALWAYS returns the SAME class. Your comment that each returned value is a new, unique class is incorrect. Your variables 'v' and 'w' both refer to the same object, which is a class (not an instance). You never instantiate an instance, so what you are doing here is manipulating values in the dictionary cImmutablyTypedPropertiesContainer.__dict__. That's why t.c doesn't invoke the getter - the getter is invoked to assigned a value to the __dict__ of an instance. Commented Feb 17 at 23:35
  • @Paul Cornelius: Each call to fImmutablyTypedPropertiesContainer does return a class with a different ID. The fundamental problem was that a property's getters and setters are only called when the getting and setting are through an instance. z=classx.propertyy results in z referring to a property object z=instancewofclassx.propertyy results in z getting the value returned by the property's getter. Commented Feb 18 at 12:45

1 Answer 1

0

The answer is that a property's getter and setter are invoked only from an instance.

The example that demonstrates this is within https://stackoverflow.com/a/7118013/1496279 .

> print ( Bar.x )
> # < property object at 0x04D37270> 
> print ( Bar () .x )
> # 0

The ( ) creates an instance.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.