V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
zhy0216
V2EX  ›  问与答

Python 有趣的一个小问题

  •  
  •   zhy0216 · 2017-03-24 14:12:12 +08:00 · 2557 次点击
    这是一个创建于 2804 天前的主题,其中的信息可能已经有所发展或是发生改变。
    令变量 a = 1, 如何在不改变 a 的内存地址的前提下, 即 id(a) 不变, 改变 a 的值.

    我无聊翻文档看到的...
    第 1 条附言  ·  2017-03-24 22:45:41 +08:00

    我当时是看的: https://benkurtovic.com/2015/01/28/python-object-replacement.html

    用的是 ctypes.memmove;

    ctypes.memmove(id(a), id(2), object.__sizeof__(a))

    虽然可以改变值, 但经常 segmentation fault

    23 条回复    2017-03-28 22:17:01 +08:00
    sivacohan
        1
    sivacohan  
       2017-03-24 14:55:53 +08:00 via Android
    求解答,这个真不知道。
    不过 1 是不可变对象,启动的时候进程就已经分配好了地址。
    这么变, a=1 , b=1 , id(a) ==id(b)==id(1)
    把 1 变了, b 不是也变了?
    Lycnir
        2
    Lycnir  
       2017-03-24 15:05:47 +08:00
    请指教~
    kgf0ry
        3
    kgf0ry  
       2017-03-24 16:39:04 +08:00 via Android
    是否可以转化为列表。利用列表下标去做呢?只是一点想法求赐教
    kindjeff
        4
    kindjeff  
       2017-03-24 17:18:49 +08:00 via iPhone
    楼主跑路了
    EchoUtopia
        5
    EchoUtopia  
       2017-03-24 17:26:26 +08:00
    1也是一个普通的 python 对象,改变 1 这个值就行了吧
    shenxgan
        6
    shenxgan  
       2017-03-24 17:30:39 +08:00
    楼主快说,请你吃瓜
    introom
        7
    introom  
       2017-03-24 17:32:07 +08:00 via Android
    假定是 cpython,你要这样改变,那得从 c 层面。
    ctypes 可以试试,或者干脆写个 c module.
    EchoUtopia
        8
    EchoUtopia  
       2017-03-24 17:38:48 +08:00
    import ctypes
    a = 1
    print(a, id(a))

    def deref(addr, typ):
    return ctypes.cast(addr, ctypes.POINTER(typ))

    deref(id(1), ctypes.c_int)[6] = 100

    print (a, id(a))

    注: python3
    Muninn
        9
    Muninn  
       2017-03-24 17:39:04 +08:00
    感觉没办法吧
    EchoUtopia
        10
    EchoUtopia  
       2017-03-24 17:39:40 +08:00
    weyou
        11
    weyou  
       2017-03-24 17:42:26 +08:00 via Android
    如果能改,那就把常量 1 所在地址指向的值改掉了,那 1 就不是 1 了, python 中所有引用到 1 的地方就是你的新值了。所以 Python 不可能有这么一个途径来实现你的要求
    weyou
        12
    weyou  
       2017-03-24 17:48:17 +08:00 via Android
    @EchoUtopia ctype 是一个另类,相当于直接调用 c 语言使用地址赋值了
    EchoUtopia
        13
    EchoUtopia  
       2017-03-24 17:50:18 +08:00
    @weyou #12 楼主的要求只是简单的不改变a的地址,改变a的值,所以改变1
    的地址就行了呗
    EchoUtopia
        14
    EchoUtopia  
       2017-03-24 17:53:26 +08:00
    @EchoUtopia #13 说错了,改变1的值
    a87150
        15
    a87150  
       2017-03-24 17:53:34 +08:00
    @EchoUtopia 感觉 ctypes 真厉害,虽然我不会用
    EchoUtopia
        16
    EchoUtopia  
       2017-03-24 18:03:31 +08:00
    @a87150 #15 我也不会用,只是很久以前解析网络包用过一次,早都忘了,感觉很高大上
    weyou
        17
    weyou  
       2017-03-24 18:03:44 +08:00 via Android
    @EchoUtopia 我在手机上的 termux/python3.5 上试了下,发生错误:
    >>> ctypes.cast(id(1), ctypes.POINTER(ctypes.c_int))[6]=100
    >>> id(a)
    Fatal Python error: non-string found in code slot

    Current thread 0x0000007fb60c2fe8 (most recent call first):
    Aborted (core dumped)
    但理论上是可行的,估计 Python 版本之间整数对象结构有差异。
    EchoUtopia
        18
    EchoUtopia  
       2017-03-24 18:07:15 +08:00
    我也不清楚
    import ctypes
    a = 1
    print(a, id(a))

    def deref(addr, typ):
    return ctypes.cast(addr, ctypes.POINTER(typ))

    deref(id(1), ctypes.c_int)[6] = 100

    print (a, id(a))
    import sys
    print(sys.version)

    输出:
    1 10771520
    100 10771520
    3.4.2 (default, Oct 8 2014, 10:45:20)
    [GCC 4.9.1]
    zhanglintc
        19
    zhanglintc  
       2017-03-24 18:24:12 +08:00
    楼主快出来, 胃口已经吊起来了
    glasslion
        20
    glasslion  
       2017-03-24 18:57:25 +08:00
    @EchoUtopia

    You shouldn't want to. This isn't Ruby, heathen.
    ----------------------------------------------------------------------------
    reddit 上第一条吐槽把 Ruby 黑出翔了
    shyling
        21
    shyling  
       2017-03-24 19:03:49 +08:00
    @glasslion hhhhhh
    aploium
        22
    aploium  
       2017-03-24 20:25:11 +08:00
    歪个楼, 如果只是要 id(a) 不变的话, 覆盖掉 id() 就行

    如果要正经做的话可能挺难的

    看一个例子

    ```python
    >>> a=1
    >>> b=1
    >>> id(a)==id(b)==id(1)
    True
    ```

    python 内部会预创建一些小整数, 之后所有用到这些小整数的地方都应用同一个内存实例

    再看一个比较大的数

    ```python
    >>> a=500000
    >>> b=500000
    >>> id(a)
    1658156658384
    >>> id(b)
    1658161244880
    >>> id(500000)
    1658161245136
    ```

    所以如果不使用某些黑魔法的话, 要做到这样, 可能会导致所有的 1 都发生改变

    当然...因为 python 本身实在是太灵活了, 你甚至可以在运行时去魔改语法树, 所以我也不知道有没有什么黑科技能实现这个东西
    mingyun
        23
    mingyun  
       2017-03-28 22:17:01 +08:00
    @aploium 小整数是 0-256 吧 记得貌似是
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5804 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 01:45 · PVG 09:45 · LAX 17:45 · JFK 20:45
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.