• Re: Is Lexical Binding The Norm Yet?

    From Stefan Ram@3:633/280.2 to All on Sat Jan 20 21:13:40 2024
    Newsgroups: comp.lang.lisp,comp.lang.python

    Lawrence D'Oliveiro <ldo@nz.invalid> writes:
    On Thu, 18 Jan 2024 23:39:26 -0500, George Neuner wrote:
    Programming with closures is more like using "prototype OO". Prototype >>systems don't have classes, but rather ANY object can be modified to
    change its set of instance data and/or methods, and can be cloned to
    create new objects of that same "type".
    That’s true of Python, too.

    Yes that's true. Forgive me guys if that's too "off topic"
    in comp.lang.lisp, but it might not be obvious how to create
    an object in Python and then attach fields or methods to it.
    So here I humbly submit a small example program to show this.

    main.py

    from types import *

    # create a new object
    def counter_object(): pass

    # attach a numeric field to the object
    counter_object.counter_value = 0

    # define a named function
    def increment_value( self ): self.counter_value += 1

    # attach the named function to the object as a method counter_object.increment_value = \
    MethodType( increment_value, counter_object )

    # call the method
    counter_object.increment_value()

    # attach an unnamed function to the object
    counter_object.get_value = \
    MethodType( lambda self: self.counter_value, counter_object )

    # call that method
    print( counter_object.get_value() )

    This program will then print "1" and a line terminator.

    Newsgroups: comp.lang.lisp,comp.lang.python


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: Stefan Ram (3:633/280.2@fidonet)
  • From Stefan Ram@3:633/280.2 to All on Sun Jan 21 18:28:12 2024
    Newsgroups: comp.lang.python

    ram@zedat.fu-berlin.de (Stefan Ram) writes:
    from types import *

    What do you mean, the cloning part is missing?

    import types

    # create a new object
    def counter_object(): pass

    # attach a numeric field to the object
    counter_object.counter_value = 0

    # define a named function
    def increment_value( self ): self.counter_value += 1

    # attach the named function to the object as a method counter_object.increment_value = \
    types.MethodType( increment_value, counter_object )

    # call the method
    counter_object.increment_value()

    # attach an unnamed function to the object
    counter_object.get_value = \
    types.MethodType( lambda self: self.counter_value, counter_object )

    # call that method
    # Prints "1".
    print( counter_object.get_value() )

    # An ad-hoc function to clone an object for the purpose of "prototype
    # inheritance". Might need to be refined for more general uses.
    def clone_object( object_ ):
    def new_object(): pass
    for attribute in dir( object_ ):
    if attribute[ :2 ]!= '__':
    if type( getattr( object_, attribute ))== types.MethodType:
    method = getattr( object_, attribute )
    function = types.FunctionType( method.__code__, {} )
    method = types.MethodType( function, new_object )
    setattr( new_object, attribute, method )
    else:
    field = getattr( object_, attribute )
    setattr( new_object, attribute, field )
    return new_object

    # create two "clones" ("clone objects")
    clone = clone_object( counter_object )
    other = clone_object( counter_object )

    # The clones "inherited" the counter field including its value.
    # Prints "1 1 1".
    print( counter_object.get_value(), clone.get_value(), other.get_value() )

    # Each object's counter has a value independent from the other object's
    # counter.
    other.increment_value()

    # Prints "1 1 2"
    print( counter_object.get_value(), clone.get_value(), other.get_value() )


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: Stefan Ram (3:633/280.2@fidonet)
  • From Stefan Ram@3:633/280.2 to All on Mon Jan 22 04:27:09 2024
    ram@zedat.fu-berlin.de (Stefan Ram) writes:
    What do you mean, the cloning part is missing?

    Most standard functions do not call the custom dunder methods
    of our prototype-based objects. Yes, the standard library was
    written for class-based objects ("3.3.12 Special method lookup")!

    However, if you just need few staples such as "str" and "print",
    you can define them yourself. (The standard library will not
    use them, but you can call them in your own code.)

    import types

    # a custom global str functions for prototype objects
    # used below
    def str( x ): return x.__str__()

    # a custom global print functions for prototype objects
    # used below
    std_print = print
    def print( *args, **kwargs ):
    return std_print( *( arg.__str__() for arg in args ), **kwargs )

    # create a new object
    def counter_object(): pass

    # attach a numeric field to the object
    counter_object.counter_value = 0

    # define a named function
    def increment_value( self ): self.counter_value += 1

    # attach the named function to the object as a method counter_object.increment_value = \
    types.MethodType( increment_value, counter_object )

    # call the method
    counter_object.increment_value()

    # attach an unnamed function to the object
    counter_object.get_value = \
    types.MethodType( lambda self: self.counter_value, counter_object )

    # attach an unnamed function to the object
    counter_object.__str__ = \
    types.MethodType\
    ( lambda self: str( self.counter_value ), counter_object )

    # call that method
    # Prints "1".
    print( counter_object.get_value() )

    # An ad-hoc function to clone an object for the purpose of "prototype
    # inheritance". Might need to be refined for more general uses.
    def clone_object( object_ ):
    def new_object(): pass
    for attribute in dir( object_ ):
    if attribute[ :2 ]!= '__' or attribute in[ '__str__' ]:
    if type( getattr( object_, attribute ))== types.MethodType:
    method = getattr( object_, attribute )
    function = types.FunctionType\
    ( method.__code__, globals() )
    method = types.MethodType( function, new_object )
    setattr( new_object, attribute, method )
    else:
    field = getattr( object_, attribute )
    setattr( new_object, attribute, field )
    return new_object

    # create two "clones" ("clone objects")
    clone = clone_object( counter_object )
    other = clone_object( counter_object )

    # The clones "inherited" the counter field including its value.
    # Prints "1 1 1".
    print( counter_object.get_value(), clone.get_value(), other.get_value() )

    # Each object's counter has a value independent from the other
    # object's counter.
    other.increment_value()

    # Prints "1 1 2"
    print( counter_object.get_value(), clone.get_value(), other.get_value() )

    # Prints "1 1 2"
    print( str( counter_object ), str( clone ), str( other ))

    # Prints "1 1 2"
    print( counter_object, clone, other )


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: Stefan Ram (3:633/280.2@fidonet)
  • From Stefan Ram@3:633/280.2 to All on Mon Jan 22 04:37:51 2024
    [a multipost, as an exception]

    ram@zedat.fu-berlin.de (Stefan Ram) writes:
    the Python standard library assumes class-based objects.)

    Since the thread is about "binding": Python can be very
    explicit when it comes to binding (should you need it).

    main.py

    import types

    x = 2

    def f():
    return x

    # prints "2"
    print( f() )

    g = types.FunctionType( f.__code__, { 'x': 4 } )

    # prints "4"
    print( g() )

    h = types.FunctionType( f.__code__, globals() )

    # prints "2"
    print( h() )

    def a():
    x = 3

    # prints "2"
    print( f() )

    # prints "3"
    print( types.FunctionType( f.__code__, locals() )() )

    # prints "2"
    print( types.FunctionType( f.__code__, globals() )() )

    a()


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: Stefan Ram (3:633/280.2@fidonet)