Class instance implementation, initializing instan

2019-07-11 21:11发布

I'm trying to the understand the initialization function in a python class system implementation, taken from this book (SICP python - reference to book section).

The init_instance (initialization) function """Return a new object with type cls, initialized with args.""" is where I'm having trouble. Below I've tried to narrow down my question, by explaining what I've understood.

def make_instance (cls): #good with this
    """return a new object instance, which is a dispatch dictionary"""
    def get_value(name):
        if name in attributes:
            return attributes[name]
        else:
            value = cls ['get'](name)
            return bind_method (value, instance)
    def set_value (name, value):
        attributes [name] = value
    attributes = {}
    instance = {'get': get_value, 'set': set_value}
    return instance

def bind_method (value, instance): #good with this
    """Return a bound method if value is callable, or value otherwise"""
    if callable (value):
        def method(*args):
            return value (instance, *args)
        return method
    else:
        return value

def make_class (attributes, base_class = None): 
    """Return a new class, which is a dispatch dictionary."""
    def get_value(name):
        if name in attributes:
            return attributes[name]
        elif base_class is not None:
            return base_class['get'](name)
    def set_value(name,value):
        attributes[name] = value
    def new(*args):
        return init_instance(cls, *args)
    cls = {'get':get_value,'set':set_value,'new':new}
    return cls

def init_instance(cls,*args): #problem here
    """Return a new object with type cls, initialized with args"""
    instance = make_instance (cls)
    init = cls ['get'] ('__init__')
    if init:                            
        init (instance, *args)          #No return function here
    return instance

Here is a call to above functions, to create a new class object called 'Jim'

def make_my_class():    #define a custom class
    pass
    return make_class({'__init__':__init__})   #return function that implements class

my_class = make_my_class()  #create a class
my_class_instance = my_class['new'] ('Jim') #create a class instance with ['new']

What I understand

Since this is an functional implementation of classes, the comparison is with inbuilt python classes. Below wherever I say Python Class/object/instance, I mean inbuilt.

  • make_instande(cls) : takes the 'class' -> cls argument (a message fxn dictionary itself) and describes the behaviour of an object , i.e. provides required properties to behave in a similar way to a python object. We can set attributes, that stay local to attributes dictionary using 'set'. Using get, if the attribute is not in object, it is looked up in class definition and a bind_method function is called.
  • bind_method(value,instance): binds a function in class definition to the object instance to emulate python methods in a python class instance. if value is not callable, returns value (python attribute from parent class).
  • make_class (attributes, base_class = None): Sets the behaviour of a class, with ability to inherit from another class. Uses get and set to in a similar fashion to make_instance, except, it doesn't require a bind_method. It uses init_instance(cls, *args) to create a new object instance with arbitrary number of arguments (for methods of the attributes). cls argument for init_instance passes class dispatch dictionary to the object instance. Hence, the object 'inherits' (for lack of a better word) the class features.
  • init_instance(cls, *args): Here I am a little unsure. Firstly, the function makes an instance with instance = make_instance(cls), the instance inherits features of the class through cls dictionary. init = cls['get']('__init__') , init is created, a statement that looks up if __init__ keyword was passed in attributes to make_class, , if init: init(instance, *args) makes args local to the instance? Returns an instance.

I can narrow down my question to -

init_instance is a return to new(*args) in make_class. Which means an instance dictionary is returned to new(*args). However, make_class returns cls which means we have to update cls somehow to contain instance properties. How is that that being done? It's most likely this statement init (instance, *args) but I don't know how to break down this statement. I haven't seen init as fn, how are arguments being passed to it?

1条回答
爷的心禁止访问
2楼-- · 2019-07-11 21:54

This code is a little tricky, so it's not surprising that you find some of it puzzling. To understand it, you need to understand closures. There's some info about closures in Python in this answer.

init_instance creates a new instance with instance = make_instance(cls), then it looks up the init method of cls, and if it exists, it calls that init method with the new instance and whatever was passed in args. Neither make_instance nor init_instance modify the cls dictionary, or the attributes dictionary that was passed to make_class when cls was created. What actually happens is that each call of make_instance creates a new attributes dict for the instance it creates, which the get and set functions in the instance dict can reference.

Your make_my_class definition doesn't make much sense. It has a redundant pass statement, and make_class({'__init__': __init__}) won't work because you haven't defined __init__ anywhere, that needs to be a function which will initialize your class instances.

Here's a modified version of your code. I've created a simple __init__ function for my_class, and added several print calls so we can get an idea of what the code's doing.

def hexid(obj):
    return hex(id(obj))

def make_instance(cls): # good with this
    """ Return a new object instance, which is a dispatch dictionary """
    def get_value(name):
        print('INSTANCE GET_VALUE', name, 'from', hexid(attributes))
        if name in attributes:
            return attributes[name]
        else:
            value = cls['get'](name)
            return bind_method(value, instance)

    def set_value(name, value):
        attributes[name] = value

    attributes = {'test': 'Default Test'}
    print('Created instance attributes', hexid(attributes))
    instance = {'get': get_value, 'set': set_value}
    return instance

def bind_method(value, instance): # good with this
    """ Return a bound method if value is callable, or value otherwise """
    if callable(value):
        def method(*args):
            return value(instance, *args)
        return method
    else:
        return value

def make_class(attributes, base_class=None): 
    """ Return a new class, which is a dispatch dictionary. """
    def get_value(name):
        print('\nCLASS GET_VALUE', name, 'from', hexid(attributes))
        if name in attributes:
            return attributes[name]
        elif base_class is not None:
            return base_class['get'](name)

    def set_value(name, value):
        attributes[name] = value

    def new(*args):
        return init_instance(cls, *args)

    print('Creating class with attributes', hexid(attributes))
    cls = {'get': get_value, 'set': set_value, 'new': new}
    return cls

def init_instance(cls, *args): # problem here
    """ Return a new object with type cls, initialized with args """
    instance = make_instance(cls)
    init = cls['get']('__init__')
    if init:
        print('Calling init of', hexid(cls), 'on', hexid(instance), 'with', args)
        init(instance, *args)          #No return here
    return instance

def make_my_class(): # define a custom class
    # Create a simple __init__ for the class
    def __init__(inst, *args):
        print('INIT', hexid(inst), args)
        inst['set']('data', args)

    # return a dict that implements class
    return make_class({'__init__': __init__})

# test

#create a class
my_class = make_my_class()

#create some class instances
jim = my_class['new']('Jim')
jim['set']('test', 'Hello')

fred = my_class['new']('Fred') 

print('CLASS', hexid(my_class))
print('\nINSTANCE', hexid(jim))
print(jim['get']('data'))
print(jim['get']('test'))

print('\nINSTANCE', hexid(fred))
print(fred['get']('data'))
print(fred['get']('test'))

output

Creating class with attributes 0xb71e67d4
Created instance attributes 0xb71373ec

CLASS GET_VALUE __init__ from 0xb71e67d4
Calling init of 0xb7137414 on 0xb71373c4 with ('Jim',)
INIT 0xb71373c4 ('Jim',)
Created instance attributes 0xb7137374

CLASS GET_VALUE __init__ from 0xb71e67d4
Calling init of 0xb7137414 on 0xb713734c with ('Fred',)
INIT 0xb713734c ('Fred',)
CLASS 0xb7137414

INSTANCE 0xb71373c4
INSTANCE GET_VALUE data from 0xb71373ec
('Jim',)
INSTANCE GET_VALUE test from 0xb71373ec
Hello

INSTANCE 0xb713734c
INSTANCE GET_VALUE data from 0xb7137374
('Fred',)
INSTANCE GET_VALUE test from 0xb7137374
Default Test
查看更多
登录 后发表回答