python reference a property like a function - python

How do you pythonically set multiple properties without referencing them individually? Below is my solution.
class Some_Class(object):
def __init__(self):
def init_property1(value): self.prop1 = value
def init_property2(value): self.prop2 = value
self.func_list = [init_property1, init_property2]
#property
def prop1(self):
return 'hey im the first property'
#prop1.setter
def prop1(self, value):
print value
#property
def prop2(self):
return 'hey im the second property'
#prop2.setter
def prop2(self, value):
print value
class Some_Other_Class(object):
def __init__(self):
myvalues = ['1 was set by a nested func','2 was set by a nested func']
some_class= Some_Class()
# now I simply set the properties without dealing with them individually
# this assumes I know how they are ordered (in the list)
# if necessary, I could use a map
for idx, func in enumerate(some_class.func_list):
func(myvalues[idx])
some_class.prop1 = 'actually i want to change the first property later on'
if __name__ == '__main__':
test = Some_Other_Class()
this became necessary to do when I had many many properties to initialize with user defined values. My code otherwise would look like a giant list of setting each property individually (very messy).
Note that many people have good answers below and I think I have reached a good solution. This is a re-edit mostly trying to clearly state the question. But, if anyone has a better approach please share!

just use the #property decorator
>>> class A:
... a=2
... #property
... def my_val(self,val=None):
... if val == None:return self.a
... self.a = val
...
>>> a=A()
>>> a.my_val
2
>>> a.my_val=7
>>> a.my_val
7
something like this?
if you only want to allow setting then dont give it a default val
>>> class A:
... a=2
... #property
... def my_val(self,val):
... self.a = val
...
>>> a=A()
>>> a.my_val
<Exception>
>>> a.my_val=7
>>> a.a
7
or if you only want to allow retrieval just ommit the 2nd arg
>>> class A:
... a=2
... #property
... def my_val(self):
... return self.a
...
...
>>> a=A()
>>> a.my_val
2
>>> a.my_val=7
<Exception>

I ... finally think I know what you're trying to do, and you don't need to do it the way you're approaching it. Let me take a stab at this.
class someclass(object):
def __init__(self):
func_list = [self.setter1, self.setter2]
value_list = [1, 2]
# These lines don't need to be this complicated.
# for ind in range(len(func_list)):
# func_list[ind](value_list[ind])
for idx, func in enumerate(func_list):
func(value_list[idx])
# Or even better
for idx, (func, val) in enumerate(zip(func_list, value_list)):
func(val)
def setter1(self, value):
self.a = value
def setter2(self, value):
self.b = value
It's worth pointing out that the idx variable and enumerate calls are superfluous in the second form, but I wasn't sure if you need that elsewhere.

If you look up the property in the object dict, you will get the property descriptor (if any), and likewise with the class; e.g.
a = SomeClass()
descriptor = a.__dict__.get('descriptor', type(a).__dict__.get('descriptor'))
Assuming that descriptor is a descriptor, it will have the following methods:
['deleter', 'fdel', 'fget', 'fset', 'getter', 'setter']
Note the fget and fset.

Related

How to modify a object getting from a property decorator?

Let's say, here is a class A. It has a private value _foo which is a json string, and access it with property getter and setter.
The code is as the following:
import json
class A(object):
_foo = '{"name":"name"}'
#property
def foo(self):
return json.loads(self._foo)
#foo.setter
def foo(self, value):
self._foo = json.dumps(value)
Usually, I use foo to do something like following:
a = A()
print(a.foo['name'])
But I'm in trouble when I want to modify it.
a = A()
print(a.foo)
# Out: {'name': 'name'}
a.foo['weight'] = 1
print(a.foo)
# Out: {'name': 'name'} # have no change
What I need to do is:
foo = a.foo
foo['weight'] = 1
a.foo = foo
print(a.foo)
Now, my question is how to implement this more intuitive and pythonic?
I think the correct way for you to do this is like this, dont save the dict as a string, but as an actual dict.
import json
class A(object):
_foo = {"name":"name"}
#property
def foo(self):
return self._foo
#foo.setter
def foo(self, value):
self._foo = value
def dump_foo(self):
jsons.dump(self.foo)
def __del__(self):
self.dump_foo()
I cant see a real reason for you to dump the json file every time, edit it, use it, and when you are done dump it.
If you still wish to print the dict (in a human readable manner) after this all you need to do is print(a.foo), due to the str conversion of a dict.

Turning an attribute into a property on an object-by-object basis?

I have a class of objects, most of whom have this one attribute which can in 95% of cases be implemented as a simple attribute. However, there are a few important edge cases where that property must be computed from data on another object.
What I'd like to be able to do is set myobj.gnarlyattribute = property(lambda self: self.container.x*self.k).
However, this doesn't seem to work:
>>> myfoo=foo()
>>> myfoo.spam
10
>>> import random
>>> myfoo.spam=property(lambda self: random.randint(0,20))
>>> myfoo.spam
<property object at 0x02A57420>
>>>
I suppose I could have gnarlyattribute always be a property which usually just has lambda self: self._gnarlyattribute as the getter, but that seems a little smelly. Any ideas?
As has already been pointed out, properties can only work at the class level, and they can't be set on instances. (Well, they can, but they don't do what you want.)
Therefore, I suggest using class inheritance to solve your problem:
class NoProps(object):
def __init__(self, spam=None):
if spam is None:
spam = 0 # Pick a sensible default
self.spam = spam
class Props(NoProps):
#property
def spam(self):
"""Docstring for the spam property"""
return self._spam
#spam.setter
def spam(self, value):
# Do whatever calculations are needed here
import random
self._spam = value + random.randint(0,20)
#spam.deleter
def spam(self):
del self._spam
Then when you discover that a particular object needs to have its spam attribute as a calculated property, make that object an instance of Props instead of NoProps:
a = NoProps(3)
b = NoProps(4)
c = Props(5)
print a.spam, b.spam, c.spam
# Prints 3, 4, (something between 5 and 25)
If you can tell ahead of time when you'll need calculated values in a given instance, that should do what you're looking for.
Alternately, if you can't tell that you'll need calculated values until after you've created the instance, that one's pretty straightforward as well: just add a factory method to your class, which will copy the properties from the "old" object to the "new" one. Example:
class NoProps(object):
def __init__(self, spam=None):
if spam is None:
spam = 0 # Pick a sensible default
self.spam = spam
#classmethod
def from_other_obj(cls, other_obj):
"""Factory method to copy other_obj's values"""
# The call to cls() is where the "magic" happens
obj = cls()
obj.spam = other_obj.spam
# Copy any other properties here
return obj
class Props(NoProps):
#property
def spam(self):
"""Docstring for the spam property"""
return self._spam
#spam.setter
def spam(self, value):
# Do whatever calculations are needed here
import random
self._spam = value + random.randint(0,20)
#spam.deleter
def spam(self):
del self._spam
Since we call cls() inside the factory method, it will make an instance of whichever class it was invoked on. Thus the following is possible:
a = NoProps(3)
b = NoProps.from_other_obj(a)
c = NoProps.from_other_obj(b)
print(a.spam, b.spam, c.spam)
# Prints 3, 3, 3
# I just discovered that c.spam should be calculated
# So convert it into a Props object
c = Props.from_other_obj(c)
print(a.spam, b.spam, c.spam)
# Prints 3, 3, (something between 3 and 23)
One or the other of these two solutions should be what you're looking for.
The magic to make properties work only exists at the class level. There is no way to make properties work per-object.

Built-in non-data version of property?

class Books():
def __init__(self):
self.__dict__['referTable'] = 1
#property
def referTable(self):
return 2
book = Books()
print(book.referTable)
print(book.__dict__['referTable'])
Running:
vic#ubuntu:~/Desktop$ python3 test.py
2
1
Books.referTable being a data descriptor is not shadowed by book.__dict__['referTable']:
The property() function is implemented as a data descriptor.
Accordingly, instances cannot override the behavior of a property.
To shadow it, instead of property built-in descriptor i must use my own descriptor. Is there a built in descriptor like property but which is non-data?
To expand on my comment, why not simply something like this:
>>> class Books():
... def __init__(self):
... self.__dict__['referTable'] = 1
... #property
... def referTable(self):
... try:
... return self.__dict__['referTable']
... except KeyError:
... return 2
...
>>> a = Books()
>>> a.referTable
1
>>> del a.__dict__['referTable']
>>> a.referTable
2
Now, I'd like to note that I don't think this is good design, and you'd be much better off using a private variable rather than accessing __dict__ directly. E.g:
class Books():
def __init__(self):
self._referTable = 1
#property
def referTable(self):
return self._referTable if self._referTable else 2
In short, the answer is no, there is no alternative to property() that works in the way you want in the Python standard library.
There is something very similar to a built-in non-data descriptor -- the class attribute:
class Books():
referTable = 'default'
def __init__(self, referTable=None):
if referTable is not None:
self.referTable = referTable
book = Books()
print(book.referTable)
# default
book.referTable = 'something specific'
print(book.referTable)
# something specific
If you need something more like a property (for example, you want a function to do some heavy-lifting the first time, but then use that first value for all future references), then you will need to build it yourself:
class OneTime(object):
def __init__(self, method):
self.name = method.__name__
self.method = method
def __get__(self, inst, cls):
if inst is None:
return self
result = self.method(inst)
inst.__dict__[self.name] = result
return result
class Books(object):
#OneTime
def referTable(self):
print 'calculating'
return 1 * 2 * 3 * 4 * 5
b = Books()
print b.__dict__
print b.referTable
print b.__dict__
print b.referTable
With the following results:
{}
calculating
120
{'referTable': 120}
120

Most elegant way to configure a class in Python?

I'm simulating a distributed system in which all nodes follow some protocol. This includes assessing some small variations in the protocol. A variation means alternative implementation of a single method. All nodes always follow the same variation, which is determined by experiment configuration (only one configuration is active at any given time). What is the clearest way to do it, without sacrificing performance?
As an experiment can be quite extensive, I clearly don't want any conditionals. Before I've just used inheritance, like:
class Node(object):
def dumb_method(self, argument):
# ...
def slow_method(self, argument):
# ...
# A lot more methods
class SmarterNode(Node):
def dumb_method(self, argument):
# A somewhat smarter variant ...
class FasterNode(SmarterNode):
def slow_method(self, argument):
# A faster variant ...
But now I need to test all possible variants and don't want an exponential number of classes cluttering the source. I also want other people peeping at the code to understand it with minimal effort. What are your suggestions?
Edit: One thing I failed to emphasize enough: for all envisioned use cases, it seems that patching the class upon configuration is good. I mean: it can be made to work by simple Node.dumb_method = smart_method. But somehow it didn't feel right. Would this kind of solution cause major headache to a random smart reader?
You can create new subtypes with the type function. You just have to give it the subclasses namespace as a dict.
# these are supposed to overwrite methods
def foo(self):
return "foo"
def bar(self):
return "bar"
def variants(base, methods):
"""
given a base class and list of dicts like [{ foo = <function foo> }]
returns types T(base) where foo was overwritten
"""
for d in methods:
yield type('NodeVariant', (base,), d)
from itertools import combinations
def subdicts(**fulldict):
""" returns all dicts that are subsets of `fulldict` """
items = fulldict.items()
for i in range(len(items)+1):
for subset in combinations(items, i):
yield dict(subset)
# a list of method variants
combos = subdicts(slow_method=foo, dumb_method=bar)
# base class
class Node(object):
def dumb_method(self):
return "dumb"
def slow_method(self):
return "slow"
# use the base and our variants to make a number of types
types = variants(Node, combos)
# instantiate each type and call boths methods on it for demonstration
print [(var.dumb_method(), var.slow_method()) for var
in (cls() for cls in types)]
# [('dumb', 'slow'), ('dumb', 'foo'), ('bar', 'slow'), ('bar', 'foo')]
You could use the __slots__ mechanism and a factory class. You would need to instantiate a NodeFactory for each experiment, but it would handle creating Node instances for you from there on. Example:
class Node(object):
__slots__ = ["slow","dumb"]
class NodeFactory(object):
def __init__(self, slow_method, dumb_method):
self.slow = slow_method
self.dumb = dumb_method
def makenode(self):
n = Node()
n.dumb = self.dumb
n.slow = self.slow
return n
an example run:
>>> def foo():
... print "foo"
...
>>> def bar():
... print "bar"
...
>>> nf = NodeFactory(foo, bar)
>>> n = nf.makenode()
>>> n.dumb()
bar
>>> n.slow()
foo
I'm not sure if you're trying to do something akin to this (allows swap-out runtime "inheritance"):
class Node(object):
__methnames = ('method','method1')
def __init__(self, type):
for i in self.__methnames:
setattr(self, i, getattr(self, i+"_"+type))
def dumb_method(self, argument):
# ...
def slow_method(self, argument):
# ...
n = Node('dumb')
n.method() # calls dumb_method
n = Node('slow')
n.method() # calls slow_method
Or if you're looking for something like this (allows running (and therefore testing) of all methods of the class):
class Node(object):
#do something
class NodeTest(Node):
def run_tests(self, ending = ''):
for i in dir(self):
if(i.endswith(ending)):
meth = getattr(self, i)
if(callable(meth)):
meth() #needs some default args.
# or yield meth if you can
You can use a metaclass for this.
If will let you create a class on the fly with method names according to every variations.
Should the method to be called be decided when the class is instantiated or after? Assuming it is when the class is instantiated, how about the following:
class Node():
def Fast(self):
print "Fast"
def Slow(self):
print "Slow"
class NodeFactory():
def __init__(self, method):
self.method = method
def SetMethod(self, method):
self.method = method
def New(self):
n = Node()
n.Run = getattr(n, self.method)
return n
nf = NodeFactory("Fast")
nf.New().Run()
# Prints "Fast"
nf.SetMethod("Slow")
nf.New().Run()
# Prints "Slow"

Python - Access a class from a list using a key

Is there any way to make a list of classes behave like a set in python?
Basically, I'm working on a piece of software that does some involved string comparison, and I have a custom class for handling the strings. Therefore, there is an instance of the class for each string.
As a result, I have a large list containing all these classes. I would like to be able to access them like list[key], where in this case, the key is a string the class is based off of (note: the string will never change once the class is instantiated, so it should be hashable).
It seems to me that I should be able to do this somewhat easily, by adding something like __cmp__ to the class, but either I'm being obtuse (likely), or I'm missing something in the docs.
Basically, I want to be able to do something like this (Python prompt example):
>>class a:
... def __init__(self, x):
... self.var = x
...
>>> from test import a
>>> cl = set([a("Hello"), a("World"), a("Pie")])
>>> print cl
set([<test.a instance at 0x00C866C0>, <test.a instance at 0x00C866E8>, <test.a instance at 0x00C86710>])
>>> cl["World"]
<test.a instance at 0x00C866E8>
Thanks!
Edit Some additional Tweaks:
class a:
... def __init__(self, x):
... self.var = x
... def __hash__(self):
... return hash(self.var)
...
>>> v = a("Hello")
>>> x = {}
>>> x[v]=v
>>> x["Hello"]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'Hello'
>>> x["Hello"]
Just write a class that behaves a bit like a mapping:
class ClassDict(object):
def __init__(self):
self.classes = {}
def add(self, cls):
self.classes[cls.__name__] = cls
def remove(self, cls):
if self.classes[cls.__name__] == cls:
del self.classes[cls.__name__]
else:
raise KeyError('%r' % cls)
def __getitem__(self, key):
return self.classes[key]
def __repr__(self):
return 'ClassDict(%s)' % (', '.join(self.classes),)
class C(object):
pass
class D(object):
pass
cd = ClassDict()
cd.add(C)
cd.add(D)
print cd
print cd['C']
Why don't you just do:
>>> v = MyStr("Hello")
>>> x = {}
>>> x[v.val]=v
>>> x["Hello"]
MyStr("Hello")
Why go through all the trouble of trying to create a hand-rolled dict that uses different keys than the ones you pass in? (i.e. "Hello" instead of MyStr("Hello")).
ex.
class MyStr(object):
def __init__(self, val):
self.val = str(val)
def __hash__(self):
return hash(self.val)
def __str__(self):
return self.val
def __repr__(self):
return 'MyStr("%s")' % self.val
>>> v = MyStr("Hello")
>>> x = {}
>>> x[str(v)]=v
>>> x["Hello"]
MyStr("Hello")
Set and dict use the value returned by an object's __hash__ method to look up the object, so this will do what you want:
>>class a:
... def __init__(self, x):
... self.var = x
...
... def __hash__(self):
... return hash(self.var)
As I remember "set" and "dict" uses also __hash__
From Python 2.x doc:
A dictionary’s keys are almost arbitrary values. Values that are not hashable, that is, values containing lists, dictionaries or other mutable types (that are compared by value rather than by object identity) may not be used as keys.
Do you want something like this
class A(object):
ALL_INSTANCES = {}
def __init__(self, text):
self.text = text
self.ALL_INSTANCES[self.text] = self
a1 = A("hello")
a2 = A("world")
print A.ALL_INSTANCES["hello"]
output:
<__main__.A object at 0x00B7EA50>

Categories

Resources