I am trying to execute javascript code in python, using pyv8 safely. At the end of the day, I have an object being used by javascript, with few fields I would like to have hidden.
I know python doesn't have encapsulation (as explained in this question), but yet, is there a way to disable access using __getattribute__?
class Context(object):
def __init__(self, debug):
self._a = ...
self._b = ...
self._c = ...
self._unlocked = False
def __enter__(self):
self._unlocked = True
def __exit__(self, exc_type, exc_val, exc_tb):
self._unlocked = False
def __getattribute__(self, name):
if object.__getattribute__(self, "_unlocked"):
return object.__getattribute__(self, name)
if name.startswith("_"):
return None
return object.__getattribute__(self, name)
So this object denies access to a "private" variables, unless unlocked using like this:
ctx = Context()
...
with ctx:
# _b is accessible here
print ctx._b
As far as there's no way to do with from javascript, nor to call __enter__ since the object is "locked".
Seems not very efficient though. Is there a better way?
You could use a property getter that restricts access?
class Context(object):
def __init__(self):
self._x = None
#property
def x(self):
"""I'm the 'x' property."""
return "Property can not be accessed."
#x.setter
def x(self, value):
self._x = value
#x.deleter
def x(self):
del self._x
More info can be found here:
https://docs.python.org/3/library/functions.html#property
Related
I have a class which has fields that would all be properties with pass through getters and setters that are validated in a certain way, such that it would satisfy the following pattern:
import numpy as np
import typing
def validate_field(value, dtype: typing.Type):
limits = np.iinfo(dtype)
assert limits.min < value < limits.max, \
"value shoule be in range: {} < {} < {}".format(limits.min, value,
limits.max)
return value
class Foo:
def __init__(self, a, b, c):
self._a = a
self._b = b
self._c = c
#property
def a(self):
return self._a
#property
def b(self):
return self._b
#property
def c(self):
return self._c
#a.setter
def a(self, value):
self._a = validate_field(value, self._a.dtype)
#b.setter
def b(self, value):
self._b = validate_field(value, self._b.dtype)
#c.setter
def c(self, value):
self._c = validate_field(value, self._c.dtype)
I want to eliminate having to type a separate property and setter decorator for each method.
I thought about using properties manually via
self._a = a
def set_a(self, value):
self._a = validate_field(value, self._a.dtype)
self.a = property(lambda self: self._a, set_a)
...
However, it seemed I would still have to manually define a function that accessed the required member for both setter and getter, so I wasn't really saving much work.
If there was a way to automatically generate such functions via naming the parameter e.g.:
def generate_function(self, parameter)
def temp(self, value):
self.parameter = validate_field(value, self.parameter.dtype)
return temp
then I wouldn't have any issues, but right now I don't see how to accomplish this.
Is there a way for me to generate these functions with a single decorator per field or automated function based property generation in __init__?
You can use getattr() and setattr(), or direct dictionary access via self.__dict__, to parametrize the attribute name:
def validated_property(name):
def getter(self):
return getattr(self, name)
def setter(self, value):
dtype = getter(self).dtype
setattr(self, name, validate_field(value, dtype))
return property(getter, setter)
then use this as
class Foo:
# ...
a = validated_property('_a')
b = validated_property('_b')
c = validated_property('_c')
etc.
If you are using Python 3.6 or newer, you can avoid having to repeat the attribute name and generate one from the name for the property (by prefixing it with _, for example), by implementing your own descriptor object, which is passed the name under which it is being assigned to a class via the descriptor.__set_name__() method:
class ValidatedProperty:
_name = None
def __set_name__(self, owner, name):
self._name = '_' + name
def __get__(self, instance, owner):
if instance is None:
return self
return getattr(instance, self._name)
def __set__(self, instance, value):
dtype = self.__get__(instance, type(instance)).dtype
setattr(instance, self._name, validate_field(value, dtype))
then use this like this:
class Foo:
# ...
a = ValidatedProperty()
b = ValidatedProperty()
c = ValidatedProperty()
I have a class as follows:
class A:
def __init__(self):
pass
def add_attr(self, name):
setattr(self, name, 'something')
How do I define custom setter, getter for self.name? I cannot use __setattr__, __getattribute__ because that will change the behaviour of add_attr too.
EDIT: the users of this class will add arbitrary number of attributes with arbitrary names:
a = A()
a.add_attr('attr1')
a.add_attr('attr2')
I want custom behavior for only these user added attributes.
Building off #Devesh Kumar Singh’s answer, I would implement it in some way like this:
class A:
def __init__(self):
self.attrs = {}
def __setattr__(self, key, value):
if key in self.attrs:
self.set_attr(key, value)
else:
object.__setattr__(self, key, value)
def __getattribute__(self, key):
if key in self.__dict__.get(attrs, {}):
return self.__dict__['get_attr'](self, key)
return object.__getattribute__(self, key)
def get_attr(self, key):
r = self.attrs[key]
# logic
return r
def set_attr(self, key, value):
# logic
self.attrs[key] = value
def add_attr(self, key, value=None):
self.attrs[key] = value
add_attr is only used to initialise the variable the first time. You could also edit __setattr__ to set all new attributes in the self.attrs rather than self.__dict__
Custom getter and setter logic? That's what a property is made for. Usually these are used to magically mask function calls and make them look like attribute access
class MyDoubler(object):
def __init__(self, x):
self._x = x
#property
def x(self):
return x * 2
#x.setter
def x(self, value):
self._x = value
>>> md = MyDoubler(10)
>>> md.x
20
>>> md.x = 20
>>> md.x
40
>>> md._x
20
But there's no rule saying you can't abuse that power to add custom behavior to your getters and setters.
class A(object):
def __init__(self):
pass
#staticmethod
def default_getter_factory(name):
def default_getter(self):
return self.name
return default_getter
#staticmethod
def default_setter_factory(name):
def default_setter(self, value):
setattr(self, name, value)
return default_setter
def add_attr(self, name, getterfactory=None, setterfactory=None):
private_name = f"_{name}"
if getterfactory is None:
getterfactory = self.__class__.default_getter_factory
if setterfactory is None:
setterfactory = self.__class__.default_setter_factory
getter, setter = getterfactory(private_name), setterfactory(private_name)
getter = property(getter)
setattr(self.__class__, name, getter)
setattr(self.__class__, name, getter.setter(setter))
That said this is all a bit silly, and chances are that whatever it is you're trying to do is a thing that shouldn't be done. Dynamic programming is all well and good, but if I were to review code that did this, I would think very long and hard about alternative solutions before approving it. This reeks of technical debt to me.
One possibility I could think of is to have a dictionary of dynamic attributes, and set and get the dynamic attributes using the dictionary
class A:
def __init__(self):
#Dictionary of attributes
self.attrs = {}
#Set attribute
def set_attr(self, name):
self.attrs[name] = 'something'
#Get attribute
def get_attr(self, name):
return self.attrs.get(name)
a = A()
a.set_attr('var')
print(a.get_attr('var'))
The output will be something
Or an alternate is to use property decorator to add arguments explicitly outside the class, as described here
class A:
def __init__(self):
pass
a = A()
#Add attributes via property decorator
a.attr_1 = property(lambda self: self.attr_1)
a.attr_2 = property(lambda self: self.attr_2)
#Assign them values and print them
a.attr_1 = 4
a.attr_2 = 6
print(a.attr_1, a.attr_2)
The output will be 4 6
I am gonna answer my own question just for reference. This is based on others' answers here. The idea is to use default __setattr__ and __getattribute__ on attributes not added through add_attr.
class A:
def __init__(self):
self.attrs = {}
def add_attr(self, name):
self.attrs[name] = 'something'
def __getattribute__(self, name):
try:
object.__getattribute__(self, 'attrs')[name] # valid only if added by user
# custom logic and return
except (KeyError, AttributeError):
return object.__getattribute__(self, name)
def __setattr__(self, name, val):
# similar to __getattribute__
In my code, I have a data store with multiple variables set to instances of a class similar to that below. (The reason is that this Interval class has lots of operator overriding functions).
class Interval(object):
def __init__(self, value):
self.value = value
data_store.a = Interval(1)
I want any references to data_store.a to return self.value rather than the Interval instance. Is this possible?
As an alternative to Malik's answer, you could make a a #property, the Pythonic equivalent of get and set for managing access to internal attributes:
class DataStore(object):
def __init__(self):
self.a = Interval(1)
#property
def a(self):
return self._a.value
#a.setter
def a(self, value):
self._a = value
Here _a is a private-by-convention attribute that stores the Interval instance. This works as you want it:
>>> store = DataStore()
>>> store.a
1
You need to extend your data store whose attribute is an interval object:
class DataStore(object):
def __init__(self):
self.a = Interval(1)
def __getattribute__(self, attr):
if attr == 'a':
return object.__getattribute__(self, 'a').value
if attr != 'a':
return object.__getattribute__(self, attr)
I want to create a class myCalc that has a constructor, with get and set methods and uses a property. I understand the get and set methods but am having trouble with putting all of the pieces together so it performs what I want it to do. What I have so far is this:
class myCalc(object):
def __init__(self):
self._ =name
def (self):
"""Get the answer doubled."""
return self._plus
def (self):
"""Get the answer squared."""
return self._times
I'd like to get as far as having the program respond with properties that double and square the number in the object. Any suggestions would be helpful.
A property object has getter, setter, and deleter methods
You should use decorators as follows:
class C(object):
def __init__(self):
self._x = None
#property
def x(self):
"""I'm the 'x' property."""
return self._x
#x.setter
def x(self, value):
self._x = value
#x.deleter
def x(self):
del self._x
If you want it readonly, just use #property and remove the two others
A simple object that has doubled and squared properties
class myCalc(object):
def __init__(self, value):
self.value = value
def __str__(self):
return str(self.value)
def dbler(self):
return self.value*2
doubled = property(dbler)
squared = property(lambda self: self.value**2)
# equivalent with the decorator
#property
def cubed(self):
return self.value**3
if __name__ == '__main__':
x = myCalc(10)
print x # 10
print x.doubled # 20
print x.squared # 100
print x.cubed # 1000
Adapted from http://docs.python.org/2/howto/descriptor.html#properties
class myCalc(object):
def_init_(self, value)
self.value=value
def_init_(self, name)
print "Welcome to myCalc!"
self._name=name
def get_name(self):
return self._name
def set_name(self, new_name):
if new_name=="":
print "You mut enter a name."
else:
self._name=new_name
print "The name has been changed."
def_str_(self)
return str(self.value)
def dbler(self):
return self.value*2
doubled=property(dbler)
squared=property(lambda self: self.value**2)
name=property(get_name, set_name)
def talk(self):
print "\nWelcome, this is," self.name
if name=='main':
x= myCalc(5)
print x
print x.doubled
print x.squared
mainline
calc=myMath("Calculator")
calc.talk()
print "\nmyCalc's name is:",
print calc.name
print "\nChanging myCalc's name."
calc.name=""
calc.talk()
raw_input("\n\nPress the enter key to exit."
Here is what I am looking to do, however I keep coming across errors. I want to be able to change the object name AND number.
I’m currently using the #property decorator to achieve “getters and setters” in a couple of my classes. I wish to be able to inherit these #property methods in a child class.
I have some Python code (specifically, I’m working in py3k) which looks vaguely like so:
class A:
#property
def attr(self):
try:
return self._attr
except AttributeError:
return ''
class B(A):
#property
def attr(self):
return A.attr # The bit that doesn't work.
#attr.setter
def attr(self, value):
self._attr = value
if __name__ == '__main__':
b = B()
print('Before set:', repr(b.attr))
b.attr = 'abc'
print(' After set:', repr(b.attr))
I have marked the part that doesn’t work with a comment. I want the base class’ attr getter to be returned. A.attr returns a property object (which is probably very close to what I need!).
Edit:
After receiving the answer below from Ned I thought up what I think is a more elegant solution to this problem.
class A:
#property
def attr(self):
try:
return self._attr
except AttributeError:
return ''
class B(A):
#A.attr.setter
def attr(self, value):
self._attr = value
if __name__ == '__main__':
b = B()
print('Before set:', repr(b.attr))
b.attr = 'abc'
print(' After set:', repr(b.attr))
The .setter decorator expects a property object which we can get using #A.attr. This means we do not have to declare the property again in the child class.
(This is the difference between working on a problem at the end of the day vs working on it at the beginning of the day!)
To override a setter in python 2 I did this:
class A(object):
def __init__(self):
self._attr = None
#property
def attr(self):
return self._attr
#attr.setter
def attr(self, value):
self._attr = value
class B(A):
#A.attr.setter
def attr(self, value):
# Do some crazy stuff with `value`
value = value[0:3]
A.attr.fset(self, value)
To understand where A.attr.fset came from see the documentation on the property class:
https://docs.python.org/2/library/functions.html#property
I think you want:
class B(A):
#property
def attr(self):
return super(B, self).attr
You mention wanting to return the parent class's getter, but you need to invoke the getter, not return it.