开发者

Change string value by function [duplicate]

This question already has answers here: How do I pass a variable by reference? (40 answers) Closed 3 years ago.

I noticed that my code has many statements like this:

开发者_如何学Go
var = "some_string"
var = some_func(var)
var = another_func(var)
print(var)  # outputs "modified_string"

It's really annoying me, it just looks awful (in the opposite of whole Python). How to avoid using that and start using it in a way like this:

var = "some_string"
modify(var, some_func)
modify(var, another_func)
print(var) # outputs "modified_string"


That might not be the most "pythonic" thing to do, but you could "wrap" your string in a list, since lists are mutable in Python. For example:

var = "string"
var_wrapper = [var]

Now you can pass that list to functions and access its only element. When changed, it will be visible outside of the function:

def change_str(lst):
    lst[0] = lst[0] + " changed!"

and you'll get:

>>> change_str(var_wrapper)
>>> var_wrapper[0]
"string changed!"

To make things a bit more readable, you could take it one step further and create a "wrapper" class:

class my_str:
    def __init__(self, my_string):
        self._str = my_string

    def change_str(self, new_str):
        self._str = new_str

    def __repr__(self):
        return self._str

Now let's run the same example:

>>> var = my_str("string")
>>> var
string
>>> var.change_str("new string!")
>>> var
new string!

* Thanks for @Error-SyntacticalRemorse for the remark of making a class.


The problem is that str, int and float (long too, if you're in Py 2.x (True and False are really ints, so them too)) are what you call 'immutable types' in Python. That means that you can't modify their internal states: all manipulations of an str (or int or float) will result in a "new" instance of the str (or whatever) while the old value will remain in Python's cache until the next garbage collection cycle.

Basically, there's nothing you can do. Sorry.


In fact, there's been at least one attempt to add a compose function to functools. I guess I understand why they didn't... But hey, that doesn't mean we can't make one ourselves:

def compose(f1, f2):
    def composition(*args, **kwargs):
        return f1(f2(*args, **kwargs))
    return composition

def compose_many(*funcs):
    if len(funcs) == 1:
        return funcs[0]
    if len(funcs) == 2:
        return compose(funcs[0], funcs[1])
    else:
        return compose(funcs[0], compose_many(*funcs[1:]))

Tested:

>>> def append_foo(s):
...     return s + ' foo'
... 
>>> def append_bar(s):
...     return s + ' bar'
... 
>>> append_bar(append_foo('my'))
'my foo bar'
>>> compose(append_bar, append_foo)('my')
'my foo bar'
>>> def append_baz(s):
...     return s + ' baz'
... 
>>> compose_many(append_baz, append_bar, append_foo)('my')
'my foo bar baz'

Come to think of it, this probably isn't the best solution to your problem. But it was fun to write.


the others already explained why that's not possible, but you could:

for modify in some_func, other_func, yet_another_func:
 var = modify(var)

or as pst said:

var = yet_another_func(other_func(some_func(var)))


There is a way to modify an immutable variable, by rewriting it in the local symbol table, however, I think that it's not very nice and should be avoided as much as possible.

def updatevar(obj, value, callingLocals=locals()):
    callingLocals[next(k for k, o in callingLocals.items() if o is obj)] = value

Another way, even less pythonic, is to use exec with a formatted instruction. It gets the variable name as a string thanks to this solution:

def getname(obj, callingLocals=locals()):
    """
    a quick function to print the name of input and value.
    If not for the default-Valued callingLocals, the function would     always
    get the name as "obj", which is not what I want.
    """
    return next(k for k, v in callingLocals.items() if v is obj)

def updatevar2(k, v, callingLocals=locals()):
    n = getname(k, callingLocals)
    exec('global {};{}={}'.format(n, n, repr(v)))

The result is as expected:

var = "some_string"
updatevar(var, "modified_string")
print(var) # outputs "modified_string"
updatevar2(var, var + '2')
print(var) # outputs "modified_string2"


Strings are immutable in python, so your second example can't work. In the first example you are binding the name var to a completely new object on each line.

Typically multiple assignments to a single name like that are a code smell. Perhaps if you posted a larger sample of code someone here could show you a better way?


I'm just gonna put this right here (since none of the answers seem to have addressed it yet)

If you're commonly repeating the same sequences of functions, consider wrapping them in a higher level function:

def metafunc(var):
    var = somefunc(var)
    var = otherfunc(var)
    var = thirdfunc(var)
    return lastfunc(var)

Then when you call the function metafunc you know exactly what's happening to your var: nothing. All you get out of the function call is whatever metafunc returns. Additionally you can be certain that nothing is happening in parts of your program that you forgot about. This is really important especially in scripting languages where there's usually a lot going on behind the scenes that you don't know about/remember.

There are benefits and drawbacks to this, the theoretical discussion is under the category of pure functional programming. Some real-world interactions (such as i/o operations) require non-pure functions because they need real-world implications beyond the scope of your code's execution. The principle behind this is defined briefly here:

http://en.wikipedia.org/wiki/Functional_programming#Pure_functions

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜