python: immutable private class variables? - python

Is there any way to translate this Java code into Python?
class Foo
{
final static private List<Thingy> thingies =
ImmutableList.of(thing1, thing2, thing3);
}
e.g. thingies is an immutable private list of Thingy objects that belongs to the Foo class rather than its instance.
I know how to define static class variables from this question Static class variables in Python but I don't know how to make them immutable and private.

In Python the convention is to use a _ prefix on attribute names to mean protected and a __ prefix to mean private. This isn't enforced by the language; programmers are expected to know not to write code that relies on data that isn't public.
If you really wanted to enforce immutability, you could use a metaclass[docs] (the class of a class). Just modify __setattr__ and __delattr__ to raise exceptions when someone attempts to modify it, and make it a tuple (an immutable list) [docs].
class FooMeta(type):
"""A type whose .thingies attribute can't be modified."""
def __setattr__(cls, name, value):
if name == "thingies":
raise AttributeError("Cannot modify .thingies")
else:
return type.__setattr__(cls, name, value)
def __delattr__(cls, name):
if name == "thingies":
raise AttributeError("Cannot delete .thingies")
else:
return type.__delattr__(cls, name)
thing1, thing2, thing3 = range(3)
class Foo(object):
__metaclass__ = FooMeta
thingies = (thing1, thing2, thing3)
other = [1, 2, 3]
Examples
print Foo.thingies # prints "(0, 1, 2)"
Foo.thingies = (1, 2) # raises an AttributeError
del Foo.thingies # raise an AttributeError
Foo.other = Foo.other + [4] # no exception
print Foo.other # prints "[1, 2, 3, 4]"
It would still technically be possible to modify these by going through the class's internal .__dict__ of attributes, but this should be enough to deter most users, it's very difficult to entirely secure Python objects.

You can't do either of those things in Python, not in the sense you do them in Java, anyway.
By convention, names prefixed with an underscore are considered private and should not be accessed outside the implementation, but nothing in Python enforces this convention. It's considered more of a warning that you're messing with an implementation detail that may change without warning in a future version of the code.

You can make it un-writeable (subtly different from immutable) by using properties, but there is no way to make it private -- that goes against Python's philosophy.
class Foo(object): # don't need 'object' in Python 3
#property
def thingies(self):
return 'thing1', 'thing2', 'thing3'
f = Foo()
print f.thingies
#('thing1', 'thing2', 'thing3')
f.thingies = 9
#Traceback (most recent call last):
# File "test.py", line 8, in <module>
# f.thingies = 9
#AttributeError: can't set attribute
Whether it's immutable or not depends on what you return; if you return a mutable object you may be able to mutate that and have those changes show up in the instance/class.
class FooMutable(object):
_thingies = [1, 2, 3]
#property
def thingies(self):
return self._thingies
foo = FooMutable()
foo.thingies.append(4)
print foo.thingies
# [1, 2, 3, 4]
This will let you mutate thingies, and because the object returned is the same object kept in the instance/class the changes will be reflected on subsequent access.
Compare that with:
class FooMutable(object):
#property
def thingies(self):
return [1, 2, 3]
foo = FooMutable()
foo.thingies.append(4)
print foo.thingies
# [1, 2, 3]
Because a brand new list is returned each time, changes to it are not reflected in subsequent accesses.

You want to look into the property() function. It allows you to define your own custom Getter and Setter for a member attribute of a class. It might look something like this:
class myClass(object):
_x = "Hard Coded Value"
def set_x(self, val): return
def get_x(self): return self._x
def del_x(self): return
x = property(get_x, set_x, del_x, "I'm an immutable property named 'x'")
I haven't used it enough to be certain whether it can be used to create something "private" so you'd have to delve into that yourself, but isinstance may help.

You can achieve the final part using type hints*. As others have said, __ achieves the private aspect well enough, so
from typing import List
from typing_extensions import Final
class Foo:
__thingies: Final[List[Thingy]] = ImmutableList.of(thing1, thing2, thing3)
I'll leave the definition of ImmutableList to you. A tuple will probably do.
*with the usual caveat that users can ignore them

Related

Difference between common method VS operator in Python data type as list [duplicate]

My question:
It seems that __getattr__ is not called for indexing operations, ie I can't use __getattr__ on a class A to provide A[...]. Is there a reason for this? Or a way to get around it so that __getattr__ can provide that functionality without having to explicitly define __getitem__, __setitem__, etc on A?
Minimal Example:
Let's say I define two nearly identical classes, Explicit and Implicit. Each creates a little list self._arr on initiation, and each defines a __getattr__ that just passes all attribute requests to self._arr. The only difference is that Explicit also defines __getitem__ (by just passing it on to self._arr).
# Passes all attribute requests on to a list it contains
class Explicit():
def __init__(self):
self._arr=[1,2,3,4]
def __getattr__(self,attr):
print('called __getattr_')
return getattr(self._arr,attr)
def __getitem__(self,item):
return self._arr[item]
# Same as above but __getitem__ not defined
class Implicit():
def __init__(self):
self._arr=[1,2,3,4]
def __getattr__(self,attr):
print('called __getattr_')
return getattr(self._arr,attr)
This works as expected:
>>> e=Explicit()
>>> print(e.copy())
called __getattr_
[1, 2, 3, 4]
>>> print(hasattr(e,'__getitem__'))
True
>>> print(e[0])
1
But this doesn't:
>>> i=Implicit()
>>> print(i.copy())
called __getattr_
[1, 2, 3, 4]
>>> print(hasattr(i,'__getitem__'))
called __getattr_
True
>>> print(i.__getitem__(0))
called __getattr_
1
>>> print(i[0])
TypeError: 'Implicit' object does not support indexing
Python bypasses __getattr__, __getattribute__, and the instance dict when looking up "special" methods for implementing language mechanics. (For the most part, special methods are ones with two underscores on each side of the name.) If you were expecting i[0] to invoke i.__getitem__(0), which would in turn invoke i.__getattr__('__getitem__')(0), that's why that didn't happen.

What really makes an object callable in python [duplicate]

I would like to do the following:
class A(object): pass
a = A()
a.__int__ = lambda self: 3
i = int(a)
Unfortunately, this throws:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: int() argument must be a string or a number, not 'A'
This only seems to work if I assign the "special" method to the class A instead of an instance of it. Is there any recourse?
One way I thought of was:
def __int__(self):
# No infinite loop
if type(self).__int__.im_func != self.__int__.im_func:
return self.__int__()
raise NotImplementedError()
But that looks rather ugly.
Thanks.
Python always looks up special methods on the class, not the instance (except in the old, aka "legacy", kind of classes -- they're deprecated and have gone away in Python 3, because of the quirky semantics that mostly comes from looking up special methods on the instance, so you really don't want to use them, believe me!-).
To make a special class whose instances can have special methods independent from each other, you need to give each instance its own class -- then you can assign special methods on the instance's (individual) class without affecting other instances, and live happily ever after. If you want to make it look like you're assigning to an attribute the instance, while actually assigning to an attribute of the individualized per-instance class, you can get that with a special __setattr__ implementation, of course.
Here's the simple case, with explicit "assign to class" syntax:
>>> class Individualist(object):
... def __init__(self):
... self.__class__ = type('GottaBeMe', (self.__class__, object), {})
...
>>> a = Individualist()
>>> b = Individualist()
>>> a.__class__.__int__ = lambda self: 23
>>> b.__class__.__int__ = lambda self: 42
>>> int(a)
23
>>> int(b)
42
>>>
and here's the fancy version, where you "make it look like" you're assigning the special method as an instance attribute (while behind the scene it still goes to the class of course):
>>> class Sophisticated(Individualist):
... def __setattr__(self, n, v):
... if n[:2]=='__' and n[-2:]=='__' and n!='__class__':
... setattr(self.__class__, n, v)
... else:
... object.__setattr__(self, n, v)
...
>>> c = Sophisticated()
>>> d = Sophisticated()
>>> c.__int__ = lambda self: 54
>>> d.__int__ = lambda self: 88
>>> int(c)
54
>>> int(d)
88
The only recourse that works for new-style classes is to have a method on the class that calls the attribute on the instance (if it exists):
class A(object):
def __int__(self):
if '__int__' in self.__dict__:
return self.__int__()
raise ValueError
a = A()
a.__int__ = lambda: 3
int(a)
Note that a.__int__ will not be a method (only functions that are attributes of the class will become methods) so self is not passed implicitly.
I have nothing to add about the specifics of overriding __int__. But I noticed one thing about your sample that bears discussing.
When you manually assign new methods to an object, "self" is not automatically passed in. I've modified your sample code to make my point clearer:
class A(object): pass
a = A()
a.foo = lambda self: 3
a.foo()
If you run this code, it throws an exception because you passed in 0 arguments to "foo" and 1 is required. If you remove the "self" it works fine.
Python only automatically prepends "self" to the arguments if it had to look up the method in the class of the object and the function it found is a "normal" function. (Examples of "abnormal" functions: class methods, callable objects, bound method objects.) If you stick callables in to the object itself they won't automatically get "self".
If you want self there, use a closure.

Wrapping big list in Python 2.7 to make it immutable

In case I have a really big list (>100k elements) that can be retrieved from some object through function call, is there a way to wrap that list to make it immutable to the caller without copying it to tuple?
In the following example I have only one list field, but the solution should work for any number of list fields.
class NumieHolder(object):
def __init__(self):
self._numies = []
def add(self, new_numie):
self._numies.append(new_numie)
#property
def numies(self):
# return numies embedded in immutable wrapper/view
return ??? numies ???
if __name__ == '__main__':
nh = NumieHolder()
for numie in xrange(100001): # >100k holds
nh.add(numie)
# messing with numies should result in exception
nh.numies[3] = 4
# but I still want to use index operator
print '100th numie:', nh.numies[99]
I would know how to write adapter that behaves that way, but I'm interested if there is already some standard solution (i.e. in standard library or widely known library) I'm not aware of.
Unfortunately, there is no such wrapper in the standard library (or other prominent libraries). The main reason is that list is supposed to be a mutable sequence type with index access. The immutable sequence type would be a tuple as you already said yourself. So usually, the standard approach to make a list immutable would be to make it into a tuple by calling tuple(lst).
This is obviously not what you want, as you want to avoid to copy all the elements. So instead, you can create a custom type that wraps the list, and offers all non-modifying methods list also supports:
class ImmutableList:
def __init__ (self, actualList):
self.__lst = actualList
def __len__ (self):
return self.__lst.__len__()
def __getitem__ (self, key):
return self.__lst.__getitem__(key)
def __iter__ (self):
return self.__lst.__iter__()
def __reversed__ (self):
return self.__lst.__reversed__()
def __contains__ (self, item):
return self.__lst.__contains__(item)
def __repr__ (self):
return self.__lst.__repr__()
def __str__ (self):
return self.__lst.__str__()
>>> original = [1, 2, 3, 4]
>>> immutable = ImmutableList(original)
>>> immutable
[1, 2, 3, 4]
>>> immutable[2]
3
>>> for i in immutable:
print(i, end='; ')
1; 2; 3; 4;
>>> list(reversed(immutable))
[4, 3, 2, 1]
>>> immutable[1] = 4
Traceback (most recent call last):
File "<pyshell#39>", line 1, in <module>
immutable[1] = 4
TypeError: 'ImmutableList' object does not support item assignment
The alternative would be to subtype list and override __setitem__ and __delitem__ to raise an exception instead, but I would suggest against that, as a subtype of list would be expected to have the same interface as list itself. ImmutableList above on the other hand is just some indexable sequence type which happens to wrap a real list itself. Apart from that, having it as a subtype of list would actually require you to copy the contents once, so wrapping is definitely better if you don’t want to recreate all those items (which seems to be your point—otherwise you could just use tuple).
See Emulating Container Types for the "special" methods you'd want to implement or override. Namely, you'd want to implement __setitem__ and __delitem__ methods to raise an exception, so the list cannot be modified.

Why does assigning to self not work, and how to work around the issue?

I have a class (list of dicts) and I want it to sort itself:
class Table(list):
…
def sort (self, in_col_name):
self = Table(sorted(self, key=lambda x: x[in_col_name]))
but it doesn't work at all. Why? How to avoid it? Except for sorting it externally, like:
new_table = Table(sorted(old_table, key=lambda x: x['col_name'])
Isn't it possible to manipulate the object itself? It's more meaningful to have:
class Table(list):
pass
than:
class Table(object):
l = []
…
def sort (self, in_col_name):
self.l = sorted(self.l, key=lambda x: x[in_col_name])
which, I think, works.
And in general, isn't there any way in Python which an object is able to change itself (not only an instance variable)?
You can't re-assign to self from within a method and expect it to change external references to the object.
self is just an argument that is passed to your function. It's a name that points to the instance the method was called on. "Assigning to self" is equivalent to:
def fn(a):
a = 2
a = 1
fn(a)
# a is still equal to 1
Assigning to self changes what the self name points to (from one Table instance to a new Table instance here). But that's it. It just changes the name (in the scope of your method), and does affect not the underlying object, nor other names (references) that point to it.
Just sort in place using list.sort:
def sort(self, in_col_name):
super(Table, self).sort(key=lambda x: x[in_col_name])
Python is pass by value, always. This means that assigning to a parameter will never have an effect on the outside of the function. self is just the name you chose for one of the parameters.
I was intrigued by this question because I had never thought about this. I looked for the list.sort code, to see how it's done there, but apparently it's in C. I think I see where you're getting at; what if there is no super method to invoke? Then you can do something like this:
class Table(list):
def pop_n(self, n):
for _ in range(n):
self.pop()
>>> a = Table(range(10))
>>> a.pop_n(3)
>>> print a
[0, 1, 2, 3, 4, 5, 6]
You can call self's methods, do index assignments to self and whatever else is implemented in its class (or that you implement yourself).

Need help on creating object with class

I need help on creating an object (a sequence of numbers) in respect to some parameters of a class. Lets say I typed in to the Python IDLE shell:
SuperLotto = make_lottery_set_type('SuperLotto', 6, (1,50))
#means user can create a 'SuperLotto' with 6 numbers in range of 1 to 50
It would make 'SuperLotto' as a new class instance of a class called 'LotteryGameType'.
This is using the code so far:
class LotterySetError(Exception):
pass
def make_lottery_set_type(name:str, size:int, minmax:tuple):
if minmax[0] > minmax[1]:
raise LotterySetError('Illegal range for tuple')
else:
name = LotteryGameType(name, size, minmax[0], minmax[1])
return name
class LotteryGameType:
def __init__(self, name, set_size, min_set_number, max_set_number):
self.name = name
self.set_size = set_size
self.min_set_number = min_set_number
self.max_set_number = max_set_number
I want to be able to create a sequence of numbers and storing it for later use so I can use it with things like overload operators (e.g. eq and ne).
I want to be able to type into the Python IDLE shell:
SuperLotto([3, 4, 19, 23, 46, 27])
This would create an object under the parameters of SuperLotto, if not under parameters of 'SuperLotto' (say more than 6 numbers), it would raise an error. Any approach would be fine. Does anyone have any ideas on how to approach this?
It sounds like what you want is for make_lottery_set_type to return a new class, presumably one that's a subclass of LotteryGameType, rather than returning an instance of that type.
This is actually pretty easy to do in Python. Class definitions are just normal code, that you can run anywhere, even in the middle of a function. And they have access to the local environment while they're running. And classes themselves are "first-class values", meaning you can pass them around and return them from functions. So:
def make_lottery_set_type(name:str, size:int, minmax:tuple):
if minmax[0] > minmax[1]:
raise LotterySetError('Illegal range for tuple')
else:
class NewLotteryGameType(LotteryGameType):
def __init__(self, numbers):
super().__init__(name, size, minmax[0], minmax[1])
self.numbers = numbers
return NewLotteryGameType
If you want to add other methods, that's the same as adding methods to any other class. For example:
def make_lottery_set_type(name:str, size:int, minmax:tuple):
if minmax[0] > minmax[1]:
raise LotterySetError('Illegal range for tuple')
else:
class NewLotteryGameType(LotteryGameType):
def __init__(self, numbers):
super().__init__(name, size, minmax[0], minmax[1])
self.numbers = numbers
def __eq__(self, rhs):
return set(self.numbers) == set(rhs.numbers)
return NewLotteryGameType
So:
>>> SuperLotto = make_lottery_set_type('SuperLotto', 6, (1,50))
>>> super1 = SuperLotto([1,2,3,4,5,6])
>>> super2 = SuperLotto([6,5,4,3,2,1])
>>> super3 = SuperLotto([7,8,9,10,11,12])
>>> super1 == super2
True
>>> super1 == super3
False
(Obviously you can define __eq__ however you want, if set-equality isn't the right rule for your use.)
If you try to inspect the values you're generating, they don't look quite as pretty as you might like. For example, you'd probably rather see SuperLotto rather than NewLotteryGameType in places like this:
>>> super1
<__main__.NewLotteryGameType at 0x10259e490>
>>> SuperLotto.__name__
'NewLotteryGameType'
For that, just add NewLotteryGameType.__name__ = name. You might also want to copy over the docstring from the parent class, or various other things.
More generally, look at functools.update_wrapper (which is designed for wrapping up functions, not classes, but many of the details are the same) for inspiration, and the inspect module docs from your Python version for all of the attributes that classes can have.
In a comment, you ask:
The only problem is that I want NewLotteryGameType to inherit the parameters such as name, set_size, min_set_number, max_set_number from LotteryGameType. So lets say I wanted to type in NewLotteryGameType.set_size in to the Python Shell. I want it to return back to me 6.
That's contradictory. If you want to inherit the instance attributes of LotteryGameType… well, you already do. For example:
>>> super1.set_size
6
If you want them to be accessible off the class, then they can't be instance attributes, they have to be class attributes. And just changing set_size to a class attribute of LotteryGameType and inheriting it won't work, because the whole point of a class attribute is that the same value shared by all instances of the class or any of its subclasses, and the subclasses all need different values.
But you could do something like this:
class LotteryGameType:
def __init__(self, min_set_number, max_set_number):
self.min_set_number = min_set_number
self.max_set_number = max_set_number
def make_lottery_set_type(lottery_name:str, size:int, minmax:tuple):
if minmax[0] > minmax[1]:
raise LotterySetError('Illegal range for tuple')
else:
class NewLotteryGameType(LotteryGameType):
name = lottery_name
set_size = size
def __init__(self, numbers):
super().__init__(minmax[0], minmax[1])
self.numbers = numbers
def __eq__(self, rhs):
return set(self.numbers) == set(rhs.numbers)
return NewLotteryGameType
(Notice that I had to rename the first make_ parameter to lottery_name so it was different from the class attribute name, because of the way scopes work.) Now, name and set_size are not instance attributes, nor are they class attributes of LotteryGameType—but they're class attributes of each NewLotteryGameType. So:
>>> SuperLotto = make_lottery_set_type('SuperLotto', 6, (1,50))
>>> SuperDuperLotto = make_lottery_set_type('SuperDuperLotto', 8, (1,100))
>>> SuperLotto.set_size
6
>>> SuperDuperLotto.set_size
8
What if you create instances of those types? Well, Python looks for attributes in the instance, then in the most-derived class, and then the base classes. So as long as you don't create instance attributes with the same name (notice that I removed the extra params, and the code that set instance attributes, from the LotteryGameType.__init__ method), it does just what you'd want:
>>> super1 = SuperLotto([1,2,3,4,5,6])
>>> super1.set_size
6
>>> duper1 = SuperDuperLotto([1,2,3,4,5,6,7,8])
>>> duper1.set_size
8
Of course this means that LotteryGameType is no longer a usable type on its own; only its subclasses are usable. But that's probably what you wanted anyway, right? You could even consider making it explicitly an abstract base class to make sure nobody accidentally tries to use a direct LotteryGameType instance.
If you're feeling brave, you might want to read up on metaclasses and see how you could adapt this whole design into use a LotteryGameMetaclass, so each new class is an instance of that metaclass instead of a subclass of the (abstract) base class. The source for the new enum module in 3.4, or the near-equivalent external flufl.enum package, might make good sample code. Then you can play with both and see how similar and how different they are.

Categories