[Python] Dynamically created Python properties not invoking getters and getters

Discussão em 'Python' iniciado por Stack, Fevereiro 17, 2025.

  1. Stack

    Stack Membro Participativo

    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:


    Continue reading...

Compartilhe esta Página