views:

58

answers:

3

I want two objects to share a single string object. How do I pass the string object from the first to the second such that any changes applied by one will be visible to the other? I am guessing that I would have to wrap the string in a sort of buffer object and do all sorts of complexity to get it to work.

However, I have a tendency to overthink problems, so undoubtedly there is an easier way. Or maybe sharing the string is the wrong way to go? Keep in mind that I want both objects to be able to edit the string. Any ideas?

Here is an example of a solution I could use:

class Buffer(object):
    def __init__(self):
        self.data = ""
    def assign(self, value):
        self.data = str(value)
    def __getattr__(self, name):
        return getattr(self.data, name)

class Descriptor(object):
    def __get__(self, instance, owner):
        return instance._buffer.data
    def __set__(self, instance, value):
        if not hasattr(instance, "_buffer"):
            if isinstance(value, Buffer):
                instance._buffer = value
                return
            instance._buffer = Buffer()
        instance._buffer.assign(value)

class First(object):
    data = Descriptor()
    def __init__(self, data):
        self.data = data
    def read(self, size=-1):
        if size < 0:
            size = len(self.data)
        data = self.data[:size]
        self.data = self.data[size:]
        return data

class Second(object):
    data = Descriptor()
    def __init__(self, data):
        self.data = data
    def add(self, newdata):
        self.data += newdata
    def reset(self):
        self.data = ""
    def spawn(self):
        return First(self._buffer)

s = Second("stuff")
f = s.spawn()
f.data == s.data
#True
f.read(2)
#"st"
f.data
# "uff"
f.data == s.data
#True
s.data
#"uff"
s._buffer == f._buffer
#True

Again, this seems like absolute overkill for what seems like a simple problem. As well, it requires the use of the Buffer class, a descriptor, and the descriptor's impositional _buffer variable.

An alternative is to put one of the objects in charge of the string and then have it expose an interface for making changes to the string. Simpler, but not quite the same effect.

A: 

hello, i am not a great expert in python, but i think that if you declare a variable in a module and add a getter/setter to the module for this variable you will be able to share it this way.

akonsu
+1  A: 

I want two objects to share a single string object.

They will, if you simply pass the string -- Python doesn't copy unless you tell it to copy.

How do I pass the string object from the first to the second such that any changes applied by one will be visible to the other?

There can never be any change made to a string object (it's immutable!), so your requirement is trivially met (since a false precondition implies anything).

I am guessing that I would have to wrap the string in a sort of buffer object and do all sorts of complexity to get it to work.

You could use (assuming this is Python 2 and you want a string of bytes) an array.array with a typecode of c. Arrays are mutable, so you can indeed alter them (with mutating methods -- and some operators, which are a special case of methods since they invoke special methods on the object). They don't have the myriad non-mutating methods of strings, so, if you need those, you'll indeed need a simple wrapper (delegating said methods to the str(...) of the array that the wrapper also holds).

It doesn't seem there should be any special complexity, unless of course you want to do something truly weird as you seem to given your example code (have an assignment, i.e., a *rebinding of a name, magically affect a different name -- that has absolutely nothing to do with whatever object was previously bound to the name you're rebinding, nor does it change that object in any way -- the only object it "changes" is the one holding the attribute, so it's obvious that you need descriptors or other magic on said object).

You appear to come from some language where variables (and particularly strings) are "containers of data" (like C, Fortran, or C++). In Python (like, say, in Java), names (the preferred way to call what others call "variables") always just refer to objects, they don't contain anything except exactly such a reference. Some objects can be changed, some can't, but that has absolutely nothing to do with the assignment statement (see note 1) (which doesn't change objects: it rebinds names).

(note 1): except of course that rebinding an attribute or item does alter the object that "contains" that item or attribute -- objects can and do contain, it's names that don't.

Alex Martelli
@Alex, right again. Maybe my wording was a little off. It's not so much that I need to change the string, but rather that I want the string data to be in sync between the two objects. It _is_ assignment where that functionality needs special treatment. As you said, strings are immutable. So my example seems to be an appropriate solution. Do you think there is a better alternative where the two objects can have the string data stay in sync? What about the alternative that I posited at the end?
Eric Snow
To be honest, my needs may or may not be peculiar. I have code that connects via XML-RPC to a remote host. I want to test my code with a local simulacrum of the remote host. To do this I was planning on tricking the whole thing by using a fake socket whose makefile method will return a fake file object (which the httplib module uses). I wanted the data in the fake socket to stay in sync with the fake file, much like the file object returned by a real socket would effectively have the same data. So there you have it.
Eric Snow
@Eric, I definitely think the last paragraph in your Q has a better architecture than trying to "sync up assignments". Wrt your second comment, neither sockets nor files have an API which involves anybody assigning strings to attributes, so I don't know why you'd want to do that -- your fake host can put data in a fake socket by calling a method that will also accumulate the data in a fake file (which is presumably a `StringIO` or thin wrapper on one), and viceversa your fake file can also add data to the fake socket the other way in its `write` method, no? How would a "shared string" help?
Alex Martelli
@Alex, thanks for the insight. I was not even using StringIO for the fake file, but rather implementing a class that provided some of the methods of the file "interface". This is where my lack of familiarity with the stdlib is killing me. As to the "file" writing back to the socket, I'll probably have to read up more on sockets. It seems like writing to the socket would affect the input side of the socket and not the output side of it.
Eric Snow
@Eric, "sleep with this under your pillow" is the standard library's mantra;-). Wrt file and socket: if A does thefile.write (then thefile.flush to make sure;-) and B does thesocket.recv then any bytes just written by A should be read by B -- not sure what you mean by input and output sides but that's the rule I believe you should be enforcing (one or two: the other one is that thesocket.send makes bytes appear for thefile.read;-).
Alex Martelli
@Alex, you're a champ to keep looking at my overly-worded comments. Thanks. It sounds like you are saying that thefile.read and thesocket.recv are pretty much the same, and likewise with thefile.write and thesocket.send. Putting data into the socket/"file" will cause the remote side to put data back in. I was saying that it's like there is a buffer that the socket holds from which thesocket.recv pulls.
Eric Snow
@Eric, the buffer is held by the file -- that's the core difference (if you look at the `makefile` code you'll see the vast majority of it is about that buffering). Real sockets have no buffer (in Python: the kernel's TCP/IP stack may of course be buffering data "below" the level that's visible to Python in order to ensure TCP's reliable, in-order delivery semantics) though you may want to use one in your simulation (best implemented, also, by holding a `StringIO` instance in your simulated socket).
Alex Martelli
@Alex, I see what you are saying. The makefile method mostly just exposes a file object for the descriptor used by the socket. Thanks again.
Eric Snow
+1  A: 

Just put your value to be shared in a list, and assign the list to both objects.

class A(object):
    def __init__(self, strcontainer):
        self.strcontainer = strcontainer
    def upcase(self):
        self.strcontainer[0] = self.strcontainer[0].upper()
    def __str__(self):
        return self.strcontainer[0]

# create a string, inside a shareable list
shared = ['Hello, World!']
x = A(shared)
y = A(shared)

# both objects have the same list
print id(x.strcontainer)
print id(y.strcontainer)

# change value in x
x.upcase()

# show how value is changed in both x and y
print str(x)
print str(y)

Prints:

10534024
10534024
HELLO, WORLD!
HELLO, WORLD!
Paul McGuire
@Paul, thanks for the feedback. Your recommendation is actually an effective solution, but less transparent. Of course, the transparency may not be relevant. Then again, I want the shared data to act like a string, rather than a list.
Eric Snow