are user defined classes mutable

后端 未结 4 846
执笔经年
执笔经年 2020-12-16 11:03

Say I want to create a class for car, tractor and boat. All these classes have an instance of engine and I want to keep t

相关标签:
4条回答
  • 2020-12-16 11:23

    User classes are considered mutable. Python doesn't have (absolutely) private attributes, so you can always change a class by reaching into the internals.

    For using your class as a key in a dict or storing them in a set, you can define a .__hash__() method and a .__eq__() method, making a promise that your class is immutable. You generally design your class API to not mutate the internal state after creation in such cases.

    For example, if your engines are uniquely defined by their id, you can use that as the basis of your hash:

    class Engine(object):
        def __init__(self, id):
            self.id = id
    
        def __hash__(self):
            return hash(self.id)
    
        def __eq__(self, other):
            if isinstance(other, self.__class__):
                return self.id == other.id
            return NotImplemented
    

    Now you can use instances of class Engine in sets:

    >>> eng1 = Engine(1)
    >>> eng2 = Engine(2)
    >>> eng1 == eng2
    False
    >>> eng1 == eng1
    True
    >>> eng1 == Engine(1)
    True
    >>> engines = set([eng1, eng2])
    >>> engines
    set([<__main__.Engine object at 0x105ebef10>, <__main__.Engine object at 0x105ebef90>])
    >>> engines.add(Engine(1))
    >>> engines
    set([<__main__.Engine object at 0x105ebef10>, <__main__.Engine object at 0x105ebef90>])
    

    In the above sample I add another Engine(1) instance to the set, but it is recognized as already present and the set didn't change.

    Note that as far as lists are concerned, the .__eq__() implementation is the important one; lists don't care if an object is mutable or not, but with the .__eq__() method in place you can test if a given engine is already in a list:

    >>> Engine(1) in [eng1, eng2]
    True
    
    0 讨论(0)
  • 2020-12-16 11:24

    All objects (with the exception of a few in the standard library, some that implement special access mechanisms using things like descriptors and decorators, or some implemented in C) are mutable. This includes instances of user defined classes, classes themselves, and even the type objects that define the classes. You can even mutate a class object at runtime and have the modifications manifest in instances of the class created before the modification. By and large, things are only immutable by convention in Python if you dig deep enough.

    0 讨论(0)
  • 2020-12-16 11:26

    I think you're confusing mutability with how python keeps references -- Consider:

    class Foo(object):
        pass
    
    t = (1,2,Foo())  # t is a tuple, :. t is immutable
    b = a[2]  # b is an instance of Foo
    b.foo = "Hello"  # b is mutable.  (I just changed it)
    print (hash(b))  # b is hashable -- although the default hash isn't very useful
    d = {b : 3}      # since b is hashable, it can be used as a key in a dictionary (or set).
    c = t            # even though t is immutable, we can create multiple references to it.
    a = [t]          # here we add another reference to t in a list.
    

    Now to your question about getting/storing a list of engines globally -- There are a few different ways to do this, here's one:

    class Engine(object):
         def __init__(self, make, model):
            self.make = make
            self.model = model
    
    class EngineFactory(object):
        def __init__(self,**kwargs):
            self._engines = kwargs
    
        def all_engines(self):
            return self._engines.values()
    
        def __call__(self,make, model):
        """ Return the same object every for each make,model combination requested """
           if (make,model) in _engines:
               return self._engines[(make,model)]
           else:
               a = self._engines[(make,model)] = Engine(make,model)
               return a   
    
     engine_factory = EngineFactory()
    
     engine1 = engine_factory('cool_engine',1.0)           
     engine2 = engine_factory('cool_engine',1.0)
     engine1 is engine2 #True !!!  They're the same engine.  Changing engine1 changes engine2
    

    The example above could be improved a little bit by having the EngineFactory._engines dict store weakref.ref objects instead of actually storing real references to the objects. In that case, you'd check to make sure the reference is still alive (hasn't been garbage collected) before you return a new reference to the object.

    0 讨论(0)
  • 2020-12-16 11:29

    EDIT: This is conceptually wrong, The immutable object in python can shed some light as to why.

    class Engine():
        def __init__(self, sn):
            self.sn = sn
    
    a = Engine(42)
    b = a
    print (a is b)
    

    prints True.

    0 讨论(0)
提交回复
热议问题