views:

185

answers:

2
+2  Q: 

Freeze in Python?

I have programmed in Python for a while, and just recently started using Ruby at work. The languages are very similar. However, I just came across a Ruby feature that I don't know how to replicate in Python. It's Ruby's freeze method.

irb(main):001:0> a = [1,2,3]
=> [1, 2, 3]
irb(main):002:0> a[1] = 'chicken'
=> "chicken"
irb(main):003:0> a.freeze
=> [1, "chicken", 3]
irb(main):004:0> a[1] = 'tuna'
TypeError: can't modify frozen array
        from (irb):4:in `[]='
        from (irb):4

Is there a way to imitate this in Python?

EDIT: I realized that I made it seem like this was only for lists; in Ruby, freeze is a method on Object so you can make any object immutable. I apologize for the confusion.

+7  A: 
>>> a = [1,2,3]
>>> a[1] = 'chicken'
>>> a
[1, 'chicken', 3]
>>> a = tuple(a)
>>> a[1] = 'tuna'
Traceback (most recent call last):
  File "<pyshell#4>", line 1, in <module>
    a[1] = 'tuna'
TypeError: 'tuple' object does not support item assignment

Also, cf. set vs. frozenset, bytearray vs. bytes.

Numbers, strings are immutable themselves:

>>> a = 4
>>> id(a)
505408920
>>> a = 42        # different object
>>> id(a)
505409528
SilentGhost
Hmm. You can freeze any object in Ruby, it's defined on Object. I don't think this is the same. Might be sufficient though.
steenslag
@steenslag: python variables are prob different from the ruby variables, it's therefore never the same thing. Integers and strings are immutable in Python, freezing dicts is trivial, as any other object.
SilentGhost
@SilentGhost: I was aware of tuples and I have edited my original question to clarify that I'm talking about all object. I contest your claim that freezing any object is trivial in Python. At least it's not as trivial as `obj.freeze()`, unless there's something I don't know.
kerkeslager
@kerkeslager: you've accepted an answer that does exactly what my code did, only in an awkward non-native way. You cannot freeze a built-in object in python of course, but that's not the what you're asking is it?
SilentGhost
+5  A: 

You could always subclass list and add the "frozen" flag which would block __setitem__ doing anything:

class freezablelist(list):
    def __init__(self,*args,**kwargs):
        list.__init__(self, *args)
        self.frozen = kwargs.get('frozen',False)

    def __setitem__(self,i, y):
        if not self.frozen:
            list.__setitem__(self, i, y)
        else:
            raise TypeError("can't modify frozen list")

    def __setslice__(self, i, j, y):
        if not self.frozen:
            list.__setslice__(self, i, j, y)
        else:
            raise TypeError("can't modify frozen list")

    def freeze(self): self.frozen = True
    def thaw(self):   self.frozen = False

Then playing with it:

>>> from freeze import freezablelist as fl
>>> a = fl([1,2,3])
>>> a[1] = 'chicken'
>>> a.freeze()
>>> a[1] = 'tuna'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "freeze.py", line 10, in __setitem__
    raise TypeError("can't modify frozen list")
TypeError: can't modify frozen list
>>> a[1:1] = 'tuna'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "freeze.py", line 16, in __setslice__
    raise TypeError("can't modify frozen list")
TypeError: can't modify frozen list
>>>
Nick T
Ah! I didn't think of using `__set__` and `__setitem__`! Good call.
kerkeslager
@kerkeslager: so how exactly does this solve your "freeze any object" problem?
SilentGhost
Yeah, this obviously isn't general (but nothing is, as no equivalent, ubiquitous mechanism in Python is), but it should be applicable to any sort of class if you subclass and override mutators (e.g. `__setitem__`, `__setattr__`, `__setslice__`)
Nick T
change lines 4-7 in self.frozen = kwargs.get('frozen', False)
Ant
@Ant: Done. I'm somewhat rusty with dictionaries, don't know all the proper methods to use when.
Nick T
@SilentGhost It doesn't exactly, but in concept it's what I was looking for. As Nick T says, it's simple enough to override all the mutators (as done above with __setitem__ and __setslice__).
kerkeslager