Python has a "really hidden encapsulation"?

D

dmytro starosud

Hello,
I'm Python beginner (from Ukraine).
I may have found the way to really hide attributes in Python class.
The whole control on this attribute is implemented inside of the
class.
*(code is below, Python 3)
You see that we can't get the reference to instance of class
"en_property" outside the class "segment", but in the inside we can.

*It seems like there is no way to get the reference to field
“en_property.v_min” or “en_property.v_max”.
And object “s” has “two” attributes “v_min”: “s.__dict__['v_min']” and
“s.v_min”.

Help me find the answer, Can I change this hidden attribute outside
the class?
Or is it just an interesting feature?
....and Python has a "really hidden encapsulation"?

code:
class en_property(property):
ptr_pget = None
ptr_pset = None

def pset(self, _class, value):
self.ptr_pset(self, _class, value)

def pget(self, _class):
return self.ptr_pget(self, _class)

def __init__(self, pget, pset):
property.__init__(self, self.pget, self.pset)
self.ptr_pget = pget
self.ptr_pset = pset

class segment():
def min_set(prop, self, p_min):
if (self.v_max is not None) and (p_min > self.v_max):
raise AttributeError('It must be: "min < max"')
else:
prop.v_min = p_min

def min_get(prop, self):
if 'v_min' in dir(prop):
return prop.v_min

def max_set(prop, self, p_max):
if (self.v_min is not None) and (p_max < self.v_min):
raise AttributeError('It must be: "min < max"')
else:
prop.v_max = p_max

def max_get(prop, self):
if 'v_max' in dir(prop):
return prop.v_max

v_min = en_property(min_get, min_set)
del min_set, min_get
v_max = en_property(max_get, max_set)
del max_set, max_get

..............................................................................Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
File "D:\_dimka\I_Master\Programs_PyPg\enca.py", line 6, in pset
self.ptr_pset(self, _class, value)
File "D:\_dimka\I_Master\Programs_PyPg\enca.py", line 19, in min_set
raise AttributeError('It must be: "min < max"')
AttributeError: It must be: "min < max"
s.__dict__['v_min'] = 2
s.__dict__['v_min'] 2
s.v_min -1
 
C

Carl Banks

Hello,
I'm Python beginner (from Ukraine).
I may have found the way to really hide attributes in Python class.
The whole control on this attribute is implemented inside of the
class.
*(code is below, Python 3)
You see that we can't get the reference to instance of class
"en_property" outside the class "segment", but in the inside we can.

*It seems like there is no way to get the reference to field
“en_property.v_min” or “en_property.v_max”.
And object “s” has “two” attributes “v_min”: “s.__dict__['v_min']” and
“s.v_min”.

Help me find the answer, Can I change this hidden attribute outside
the class?
Or is it just an interesting feature?
...and Python has a "really hidden encapsulation"?
[snip example]


Well, you say you can access the attribute from inside the class, but
you didn't show any examples of it. What would happen if you had a
method that tried to run this: "self.v_min = 2"? I didn't check it
but it looks like it'd raise the exception, too.

This is what the term "data hiding" means to me, and, in my
experience, with most people talking about OOP: they usually mean
hiding it from code outside the class while keeping it open to code
inside the class. Which means "self.v_min=2" would work, but
"s.v_min=2" would not. What you've done is to hide it from everyone,
so I wouldn't call it Data Hiding(tm), even if you have, technically,
hidden data.

The term I use for what you did here is "enforcing an invariant", and
I think it is an interesting feature and occasionally indispensible,
but it's also something Python's always been able to do. Even in
Python 1.5, before properties and such, you could override __setattr__
to enforce invariants.


Carl Banks
 
D

dmytro starosud

Thanks for answer,

Attribute accessing inside the class is implemented by getters and
setters: "min_set", "min_get", "max_set" and "max_get".
You can put your logic to this functions and parameter "prop" is the
direct reference to this property.
I can create a "really read-only attribute", and other developers
which use my class can't affect this attribute, they can only using
getters and setters written inside the class.
I realize that attribute changes can be just in getters and setters,
but this restriction it's insignificant.

In case with using __setattr__: you always can access an attribute by
__dict__ and implementation of this method also use __dict__,
but in case with "hidden property" you can rich the reference to
property inside the class (it is parapeter "prop") and outside the
class you cannot.

I didn't want to affirm that this way is like C# or Java
encapsulation, but I think the fact that physical reference to
attribute hides is really interesting and is the way to do
encapsulating like in C# or similar others.

*I have been locking for encapsulating in Python because all say that
is no way to "really" hide attributes. I think I disproved this.
 
A

Arnaud Delobelle

dmytro starosud said:
Hello,
I'm Python beginner (from Ukraine).
I may have found the way to really hide attributes in Python class.
The whole control on this attribute is implemented inside of the
class.
*(code is below, Python 3)
You see that we can't get the reference to instance of class
"en_property" outside the class "segment", but in the inside we can.

*It seems like there is no way to get the reference to field
“en_property.v_min†or “en_property.v_maxâ€.
And object “s†has “two†attributes “v_minâ€: “s.__dict__['v_min']†and
“s.v_minâ€.

Help me find the answer, Can I change this hidden attribute outside
the class?
Or is it just an interesting feature?
...and Python has a "really hidden encapsulation"?

code:
class en_property(property):
ptr_pget = None
ptr_pset = None

def pset(self, _class, value):
self.ptr_pset(self, _class, value)

def pget(self, _class):
return self.ptr_pget(self, _class)

def __init__(self, pget, pset):
property.__init__(self, self.pget, self.pset)
self.ptr_pget = pget
self.ptr_pset = pset

class segment():
def min_set(prop, self, p_min):
if (self.v_max is not None) and (p_min > self.v_max):
raise AttributeError('It must be: "min < max"')
else:
prop.v_min = p_min

def min_get(prop, self):
if 'v_min' in dir(prop):
return prop.v_min

def max_set(prop, self, p_max):
if (self.v_min is not None) and (p_max < self.v_min):
raise AttributeError('It must be: "min < max"')
else:
prop.v_max = p_max

def max_get(prop, self):
if 'v_max' in dir(prop):
return prop.v_max

v_min = en_property(min_get, min_set)
del min_set, min_get
v_max = en_property(max_get, max_set)
del max_set, max_get

............................................................................Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
File "D:\_dimka\I_Master\Programs_PyPg\enca.py", line 6, in pset
self.ptr_pset(self, _class, value)
File "D:\_dimka\I_Master\Programs_PyPg\enca.py", line 19, in min_set
raise AttributeError('It must be: "min < max"')
AttributeError: It must be: "min < max"
s.__dict__['v_min'] = 2
s.__dict__['v_min'] 2
s.v_min -1

One big problem is that the value of v_min and v_max is shared by all
instances of segment:
5

So it's not very useful (I suppose it's some kind of "class property").
Moreover I can still make v_min > v_max:
False
 
D

dmytro starosud

:-0
very interesting!
I've tried to run something like this:

......................................................................
class en_property(property):
ptr_pget = None
ptr_pset = None

def pset(self, _class, value):
if (self.ptr_pset is None or type(value) == tuple):
self.ptr_pset = value[1]
self.ptr_pget = value[0]
else:
self.ptr_pset(self, _class, value)

def pget(self, _class):
return self.ptr_pget(self, _class)

def __init__(self, pget, pset):
property.__init__(self, self.pget, self.pset)
self.ptr_pget = pget
self.ptr_pset = pset

class segment():
def __new__(self):
class _segment():
def min_set(prop, self, p_min):
if (self.v_max is not None) and (p_min > self.v_max):
raise AttributeError('It must be: "min < max"')
else:
prop.v_min = p_min

def min_get(prop, self):
if 'v_min' in dir(prop):
return prop.v_min

def max_set(prop, self, p_max):
if (self.v_min is not None) and (p_max < self.v_min):
raise AttributeError('It must be: "min < max"')
else:
prop.v_max = p_max

def max_get(prop, self):
if 'v_max' in dir(prop):
return prop.v_max

def class_set(prop, self, value):
raise AttributeError('Cannot change "__class__"')

def class_get(prop, self):
class segment():
v_min, v_max = None, None
return segment

v_min = en_property(min_get, min_set)
v_max = en_property(max_get, max_set)
__class__ = en_property(class_get, class_set)
del min_get, min_set, max_get, max_set, class_get,
class_set
obj = _segment()
return obj
............................................................................................................................................

I've even override attribute __class__ (it is an property),
and code "s.__class__.v_min.v_min" raises an exception.
but we still can change the value of property using direct link:
I think I'm being realized that Python allows to do everything.
Maybe I will not try to find "really hidden encapsulation". :)

p.s. what do you think about the presence of two fields v_min (in
first message):
“s.__dict__['v_min']” and “s.v_min”?
 
A

Arnaud Delobelle

dmytro starosud said:
I think I'm being realized that Python allows to do everything.
Maybe I will not try to find "really hidden encapsulation". :)

I think it's a wise decision :)

Just to challenge you a bit, here is another (doomed) attempt at having
private attributes for object instances:


def private_maker():
class Private: pass
privmap = {}
def private(f):
def wrapper(self, *args, **kwargs):
priv = privmap.setdefault(self, Private())
return f(self, priv, *args, **kwargs)
return wrapper
return private

private = private_maker()

class A:
@private
def __init__(self, private, x):
private.x = x
@property
@private
def x(self, private):
return private.x

del private

a = A(2)


Can you change the value of a.x?

(Hint: my shortest solution is of the form A.*.*[*].*[*].x = 3)
p.s. what do you think about the presence of two fields v_min (in
first message):
“s.__dict__['v_min']†and “s.v_min�

Are you referring to the fact that in Python, if an attribute is a
property, the __dict__ lookup on the instance is not performed? As in:
.... @property
.... def x(self): return 42
....
a = A()
a.__dict__['x'] = 24
a.x 42
a.__dict__['x']
24

This is documented, but I actually don't know the reason for it.
 
E

Emile van Sebille

On 10/23/2010 11:51 AM Arnaud Delobelle said...
Just to challenge you a bit, here is another (doomed) attempt at having
private attributes for object instances:


def private_maker():
class Private: pass
privmap = {}
def private(f):
def wrapper(self, *args, **kwargs):
priv = privmap.setdefault(self, Private())
return f(self, priv, *args, **kwargs)
return wrapper
return private

private = private_maker()

class A:
@private
def __init__(self, private, x):
private.x = x
@property
@private
def x(self, private):
return private.x

del private

a = A(2)


Can you change the value of a.x?

(Hint: my shortest solution is of the form A.*.*[*].*[*].x = 3)



I'm obviously missing something:

ActivePython 2.6.1.1 (ActiveState Software Inc.) based on
Python 2.6.1 (r261:67515, Dec 5 2008, 13:58:38) [MSC v.1500 32 bit
(Intel)] on
win32
Type "help", "copyright", "credits" or "license" for more information..... class Private: pass
.... privmap = {}
.... def private(f):
.... def wrapper(self, *args, **kwargs):
.... priv = privmap.setdefault(self, Private())
.... return f(self, priv, *args, **kwargs)
.... return wrapper
.... return private
........ @private
.... def __init__(self, private, x):
.... private.x = x
.... @property
.... @private
.... def x(self, private):
.... return private.x
....

Emile
 
A

Arnaud Delobelle

Emile van Sebille said:
On 10/23/2010 11:51 AM Arnaud Delobelle said...
Just to challenge you a bit, here is another (doomed) attempt at having
private attributes for object instances:
[...]
I'm obviously missing something:

ActivePython 2.6.1.1 (ActiveState Software Inc.) based on
Python 2.6.1 (r261:67515, Dec 5 2008, 13:58:38) [MSC v.1500 32 bit
(Intel)] on
win32
Type "help", "copyright", "credits" or "license" for more information.... class Private: pass
... privmap = {}
... def private(f):
... def wrapper(self, *args, **kwargs):
... priv = privmap.setdefault(self, Private())
... return f(self, priv, *args, **kwargs)
... return wrapper
... return private
...... @private
... def __init__(self, private, x):
... private.x = x
... @property
... @private
... def x(self, private):
... return private.x
...

Emile

Sorry, I forgot to mention that this is Python 3 code. In Python 2.X,
the "class A:" statement makes the class old-style. To try this in
Python 2.X, replace

class A:
...

with

class A(object):
...

to make the class new-style.
 
E

Emile van Sebille

On 10/23/2010 11:51 AM Arnaud Delobelle said...
Can you change the value of a.x?

(Hint: my shortest solution is of the form A.*.*[*].*[*].x = 3)

A.x,a.x = a.x,3
 
A

Arnaud Delobelle

Emile van Sebille said:
On 10/23/2010 11:51 AM Arnaud Delobelle said...
Can you change the value of a.x?

(Hint: my shortest solution is of the form A.*.*[*].*[*].x = 3)

A.x,a.x = a.x,3

I knew that was going to come next! That'll teach me not to specify the
problem precisely :) The challenge is to actually change the value of
the private 'x', so that 'a.x' will evaluate to 3 with neither 'A' nor
'a' being tampered with. Changing 'A.x' breaks every instance of 'A':
3

So it is not a very robust solution :) It is possible to do this:
a, b = A(2), A(40)
[some expression] = 3
a.x, b.x (3, 40)
a.__class__ == b.__class__ == A
True

Everything works as before, only the private 'x' of 'a' has been changed
to 3.

I wanted to illustrate that it is very difficult (impossible?) to hide
objects in Python.
 
A

Aahz

Are you referring to the fact that in Python, if an attribute is a
property, the __dict__ lookup on the instance is not performed? As in:
... @property
... def x(self): return 42
...
a = A()
a.__dict__['x'] = 24
a.x 42
a.__dict__['x']
24

This is documented, but I actually don't know the reason for it.

Because otherwise you would be able to overwrite the property with a
value.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,008
Latest member
HaroldDark

Latest Threads

Top