Create a 'standard property' for class attributes in Python - python

I have a class, that is used to translate binary stream to human readable. I want to translate it both ways, because I send and receive binary messages. Attributes of this class are made mostly the same way - take the bytes from startbyte to stopbyte and decode them - so I made a decision to use a property to do that. But can I make a general "property" that will be used when defining my class attributes?
class Packet(object):
def __init__(self, data):
self.data = data
def standard_getter(startbyte, stopbyte):
def getter(self):
return decode(self.data[startbyte:stopbyte])
return getter
def standard_setter(startbyte, stopbyte):
def setter(self, value):
self.data[startbyte:stopbyte] = encode(value)
return setter
# the way I define properties by now:
protocol_type = property(standard_getter(16, 18), standard_setter(16, 18))
protocol_sub_type = property(standard_getter(18, 20), standard_setter(18, 20))
# the way I would like to do it:
protocol_type = property(standard_property(16, 18))
# or
protocol_type = standard_property(16, 18)
I tried to define a function, that takes two arguments and returns property(getter, setter), but always I'm stuck in giving "self" instance to the function. Is there a nice way I can make it?

Have your function produce both the getter and setter, and return the property object for those two functions:
def standard_property(startbyte, stopbyte):
def getter(self):
return decode(self.data[startbyte:stopbyte])
def setter(self, value):
self.data[startbyte:stopbyte] = encode(value)
return property(getter, setter)
Then use the return value directly:
protocol_type = standard_property(16, 18)
protocol_sub_type = standard_property(18, 20)
Note that the standard_property() function doesn't even need to live in your class; it could be a top-level function too:
>>> def standard_property(startbyte, stopbyte):
... def getter(self):
... return decode(self.data[startbyte:stopbyte])
... def setter(self, value):
... self.data[startbyte:stopbyte] = encode(value)
... return property(getter, setter)
...
>>> encode = lambda v: list(v)
>>> decode = lambda v: ''.join(v)
>>> class Packet(object):
... def __init__(self, data):
... self.data = data
... protocol_type = standard_property(16, 18)
... protocol_sub_type = standard_property(18, 20)
...
>>> p = Packet(list('foo bar baz spam ham eggs'))
>>> p.protocol_type
' h'
>>> p.protocol_sub_type
'am'
>>> p.protocol_type = '_c'
>>> p.protocol_sub_type = 'an'
>>> ''.join(p.data)
'foo bar baz spam_can eggs'

Related

Can decorator wrap setter and getter in python?

I want to build various setter and getter. Fot not copy and paste the code, I thought something to solve it. Can decorator do it?
#property
def !!variable_name!!(self):
return self.__!!variable_name!!
#!!variable_name!!.setter
def !!variable_name!!(self, input):
self.__!!variable_name!! = input
Is it possible like macro in C?
It's unclear why you would want to do something like this—create a property with setter that ignores its value argument—but the answer is "Yes", you can do it by creating a function that returns a custom property object:
However you can't use # syntax to apply it. Instead you have to utilize it as shown:
def attribute_property(name, input_value):
STORAGE_NAME = '_' + name
#property
def prop(self):
return getattr(self, STORAGE_NAME)
#prop.setter
def prop(self, ignored):
setattr(self, STORAGE_NAME, input_value)
return prop
# EXAMPLE USAGE
class Person(object):
name = attribute_property('name', 'Monty')
def __init__(self, name, age):
self.name = name # ignores value of passed "name" argument!
self.age = age
user = Person('Rodrigo', 42)
print('user.name: {!r}'.format(user.name))
print('user.age: {!r}'.format(user.age))
Output:
user.name: 'Monty'
user.age: 42
Simple answer: Yes, that's possible using the descriptor protocol. For example you want to save variables with a leading underscore and access them without the leading underscore such a descriptor would work:
from six import string_types
class DescriptorSingleLeadingUnderscore(object):
def __init__(self, attr, doc=""):
if not isinstance(attr, string_types):
# Not a string so take the documentation (if avaiable) and name
# from the method.
if attr.__doc__:
doc = attr.__doc__
attr = attr.__name__
self.__doc__ = doc # Set the documentation of the instance.
self.attr = '_' + attr # Add leading underscore to the attribute name
def __get__(self, instance, owner=None):
if instance is None:
return self
return getattr(instance, self.attr, None)
def __set__(self, instance, value):
setattr(instance, self.attr, value)
def __delete__(self, instance):
delattr(instance, self.attr)
class X(object):
someproperty = DescriptorSingleLeadingUnderscore('someproperty')
someproperty1 = DescriptorSingleLeadingUnderscore('someproperty1')
someproperty2 = DescriptorSingleLeadingUnderscore('someproperty2')
someproperty3 = DescriptorSingleLeadingUnderscore('someproperty3')
#DescriptorSingleLeadingUnderscore
def it_also_works_as_decorator(self):
pass # this code is never executed!
And a test case:
>>> x = X()
>>> x.someproperty = 100
>>> x.someproperty
100
>>> x._someproperty
100
>>> x.it_also_works_as_decorator = 100
>>> x.it_also_works_as_decorator
100
>>> x._it_also_works_as_decorator
100

Automatically apply getter and setter to new object variable

Ok, lets say I have a really simple class i.e.:
class Test(object):
pass
What I would like to do is to define some default setter and getter methods
which are automatically applied to a new object member at creation time. In the example below a.x should always be uppercase, i.e.:
a = Test()
a.x = "foo"
print a.x
>>> FOO
If I create x within the class I would get this behavior like this:
class Test(object):
def __init__(self):
self._x = ""
#property
def x(self):
return self._x
#x.setter(self, string):
self._x = string.upper()
So is there any possibility to do this without defining setter and getter methods for each member ?? Thank a lot.
EDIT: With creation time I meant the creation time of a.x not of the class instance.
The simplest way is probably to override __setattr__, and change any string values to uppercase:
>>> class Test(object):
def __setattr__(self, attr, val):
if isinstance(val, basestring):
val = val.upper()
super(Test, self).__setattr__(attr, val)
>>> t = Test()
>>> t.x = 'foo'
>>> t.x
'FOO'
Subclass a dict;
In [1]: %cpaste
Pasting code; enter '--' alone on the line to stop or use Ctrl-D.
:class Struct(dict):
: """A dict subclass where you can simply use a dot to access attributes."""
:
: def __getattr__(self, name):
: return self[name]
:
: def __setattr__(self, name, value):
: self[name] = value
:--
In [2]: a = Struct()
In [3]: a.x = "foo"
In [4]: a.x
Out[4]: 'foo'
In [5]: a.length = 14
In [6]: a
Out[6]: {'length': 14, 'x': 'foo'}
That sounds like a use-case for pythons Descriptor Proctocol.
class WithDescriptors:
x = UpperCaseDescriptor()
y = UpperCaseDescriptor()
z = UpperCaseDescriptor()
class UperCaseDescriptor(object):
def __init__(self):
self.val = ''
def __get__(self, obj, objtype):
return self.val.upper()
def __set__(self, obj, val):
self.val = val
Thats just an outline and i didnt test the code to work!
If you want to extend such behaviour to every attribute of an instance,
even which are not existent, you should consider metaclasses.

How to define similar class-functions related to different class-properties inside __init__

I have a class like this:
class MyClass(object):
def f_1(self,x):
return foo(x, self.property_1)
def f_2(self,x):
return foo(x, self.property_2)
The idea is that multiple functions f_n have a common structure, but depend on different properties property_n of the class.
I look for a more compact way to define those f_n in the __init__? I think of something like
class MyClass(object):
def __init__(self):
self.f_1 = self.construct_function(self.property_1)
self.f_2 = self.construct_function(self.property_2)
def construct_function(self, property):
# ???
That is what I have in mind, but I dont know how to define this construct_function. It is important that 'property' is of a point-by-value type.
Edit:
I simplified Martijn's very good answer to this solution, which works fine:
def construct_function(property_name):
def f_n(self, x):
return foo(x, getattr(self, property_name))
return f_n
class MyClass2(object):
f_1 = construct_function('property_1')
f_2 = construct_function('property_2')
Just wanted to mention it here, as multiline comments are not allowed...
If you want to generate these methods per class, use a class decorator:
def property_functions(**properties):
def construct_method(prop):
def f_n(self):
return foo(getattr(self, prop))
return f_n
def class_decorator(cls):
for name, prop in properties.iteritems():
setattr(cls, name, construct_method(prop))
return cls
return class_decorator
then use it like:
#property_functions(f_1='property_1', f_2='property_2')
class MyClass(object):
property_1 = 'foo'
property_2 = 'bar'
Demonstration:
>>> def foo(value): print value
...
>>> #property_functions(f_1='property_1', f_2='property_2')
... class MyClass(object):
... property_1 = 'foo'
... property_2 = 'bar'
...
>>> mc = MyClass()
>>> mc.f_1()
foo
>>> mc.f_2()
bar
You can have a look at getattr or getattribute . They allow you dynamically create and reference attributes. For ex
It works something like this:
class foo:
def __init__(self):
self.a = "a"
def __getattr__(self, attribute):
return "You asked for %s, but I'm giving you default" % attribute
>>> bar = foo()
>>> bar.a
'a'
>>> bar.b
"You asked for b, but I'm giving you default"
>>> getattr(bar, "a")
'a'
>>> getattr(bar, "b")
"You asked for b, but I'm giving you default"

Using #property decorator on dicts

I'm trying to use Python's #property decorator on a dict in a class. The idea is that I want a certain value (call it 'message') to be cleared after it is accessed. But I also want another value (call it 'last_message') to contain the last set message, and keep it until another message is set. In my mind, this code would work:
>>> class A(object):
... def __init__(self):
... self._b = {"message": "",
... "last_message": ""}
... #property
... def b(self):
... b = self._b
... self._b["message"] = ""
... return b
... #b.setter
... def b(self, value):
... self._b = value
... self._b["last_message"] = value["message"]
...
>>>
However, it doesn't seem to:
>>> a = A()
>>> a.b["message"] = "hello"
>>> a.b["message"]
''
>>> a.b["last_message"]
''
>>>
I'm not sure what I have done wrong? It seems to me like #property doesn't work like I would expect it to on dicts, but maybe I'm doing something else fundamentally wrong?
Also, I know that I could just use individual values in the class. But this is implemented as a session in a web application and I need it to be a dict. I could either make this work, or make the whole session object to pretend it's a dict, or use individual variables and hack it into workingness throughout the rest of the code base. I would much rather just get this to work.
class MyDict(dict):
def __setitem__(self, key, value):
if key == 'message':
super().__setitem__('message', '')
super().__setitem__('last_message', value)
else:
super().__setitem__(key, value)
class A(object):
def __init__(self):
self._b = MyDict({"message": "",
"last_message": ""})
#property
def b(self):
return self._b
a = A()
a.b['message'] = 'hello'
print(a.b['message'])
# ''
print(a.b['last_message'])
# hello
As I think you've discovered, the reason why your setter wasn't working is because
a.b['message']='hello'
first accesses a.b, which calls the b property's getter, not its setter. The getter returns the dict self._b. Then self._b['message']='hello' causes the dict's __setitem__ is called .
So to fix the problem, you need a special dict (like MyDict).
I may be missing what you are trying to do here, but does this solve your problem?
class A(object):
def __init__(self):
self._b = {'message':'',
'last_message': ''}
#property
def b(self):
b = self._b.copy()
self._b['message'] = ''
return b
#b.setter
def b(self, value):
self._b['message'] = value
self._b['last_message'] = value
if __name__ == "__main__":
a = A()
a.b = "hello"
print a.b
print a.b
print a.b["last_message"]
$ python dictPropTest.py
{'last_message': 'hello', 'message': 'hello'}
{'last_message': 'hello', 'message': ''}
hello

How to create a class instance without calling initializer?

Is there any way to avoid calling __init__ on a class while initializing it, such as from a class method?
I am trying to create a case and punctuation insensitive string class in Python used for efficient comparison purposes but am having trouble creating a new instance without calling __init__.
>>> class String:
def __init__(self, string):
self.__string = tuple(string.split())
self.__simple = tuple(self.__simple())
def __simple(self):
letter = lambda s: ''.join(filter(lambda s: 'a' <= s <= 'z', s))
return filter(bool, map(letter, map(str.lower, self.__string)))
def __eq__(self, other):
assert isinstance(other, String)
return self.__simple == other.__simple
def __getitem__(self, key):
assert isinstance(key, slice)
string = String()
string.__string = self.__string[key]
string.__simple = self.__simple[key]
return string
def __iter__(self):
return iter(self.__string)
>>> String('Hello, world!')[1:]
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
String('Hello, world!')[1:]
File "<pyshell#1>", line 17, in __getitem__
string = String()
TypeError: __init__() takes exactly 2 positional arguments (1 given)
>>>
What should I replace string = String(); string.__string = self.__string[key]; string.__simple = self.__simple[key] with to initialize the new object with the slices?
EDIT:
As inspired by the answer written below, the initializer has been edited to quickly check for no arguments.
def __init__(self, string=None):
if string is None:
self.__string = self.__simple = ()
else:
self.__string = tuple(string.split())
self.__simple = tuple(self.__simple())
When feasible, letting __init__ get called (and make the call innocuous by suitable arguments) is preferable. However, should that require too much of a contortion, you do have an alternative, as long as you avoid the disastrous choice of using old-style classes (there is no good reason to use old-style classes in new code, and several good reasons not to)...:
class String(object):
...
bare_s = String.__new__(String)
This idiom is generally used in classmethods which are meant to work as "alternative constructors", so you'll usually see it used in ways such as...:
#classmethod
def makeit(cls):
self = cls.__new__(cls)
# etc etc, then
return self
(this way the classmethod will properly be inherited and generate subclass instances when called on a subclass rather than on the base class).
A trick the standard pickle and copy modules use is to create an empty class, instantiate the object using that, and then assign that instance's __class__ to the "real" class. e.g.
>>> class MyClass(object):
... init = False
... def __init__(self):
... print 'init called!'
... self.init = True
... def hello(self):
... print 'hello world!'
...
>>> class Empty(object):
... pass
...
>>> a = MyClass()
init called!
>>> a.hello()
hello world!
>>> print a.init
True
>>> b = Empty()
>>> b.__class__ = MyClass
>>> b.hello()
hello world!
>>> print b.init
False
But note, this approach is very rarely necessary. Bypassing the __init__ can have some unexpected side effects, especially if you're not familiar with the original class, so make sure you know what you're doing.
Using a metaclass provides a nice solution in this example. The metaclass has limited use but works fine.
>>> class MetaInit(type):
def __call__(cls, *args, **kwargs):
if args or kwargs:
return super().__call__(*args, **kwargs)
return cls.__new__(cls)
>>> class String(metaclass=MetaInit):
def __init__(self, string):
self.__string = tuple(string.split())
self.__simple = tuple(self.__simple())
def __simple(self):
letter = lambda s: ''.join(filter(lambda s: 'a' <= s <= 'z', s))
return filter(bool, map(letter, map(str.lower, self.__string)))
def __eq__(self, other):
assert isinstance(other, String)
return self.__simple == other.__simple
def __getitem__(self, key):
assert isinstance(key, slice)
string = String()
string.__string = self.__string[key]
string.__simple = self.__simple[key]
return string
def __iter__(self):
return iter(self.__string)
>>> String('Hello, world!')[1:]
<__main__.String object at 0x02E78830>
>>> _._String__string, _._String__simple
(('world!',), ('world',))
>>>
Addendum:
After six years, my opinion favors Alex Martelli's answer more than my own approach. With meta-classes still on the mind, the following answer shows how the problem can be solved both with and without them:
#! /usr/bin/env python3
METHOD = 'metaclass'
class NoInitMeta(type):
def new(cls):
return cls.__new__(cls)
class String(metaclass=NoInitMeta if METHOD == 'metaclass' else type):
def __init__(self, value):
self.__value = tuple(value.split())
self.__alpha = tuple(filter(None, (
''.join(c for c in word.casefold() if 'a' <= c <= 'z') for word in
self.__value)))
def __str__(self):
return ' '.join(self.__value)
def __eq__(self, other):
if not isinstance(other, type(self)):
return NotImplemented
return self.__alpha == other.__alpha
if METHOD == 'metaclass':
def __getitem__(self, key):
if not isinstance(key, slice):
raise NotImplementedError
instance = type(self).new()
instance.__value = self.__value[key]
instance.__alpha = self.__alpha[key]
return instance
elif METHOD == 'classmethod':
def __getitem__(self, key):
if not isinstance(key, slice):
raise NotImplementedError
instance = self.new()
instance.__value = self.__value[key]
instance.__alpha = self.__alpha[key]
return instance
#classmethod
def new(cls):
return cls.__new__(cls)
elif METHOD == 'inline':
def __getitem__(self, key):
if not isinstance(key, slice):
raise NotImplementedError
cls = type(self)
instance = cls.__new__(cls)
instance.__value = self.__value[key]
instance.__alpha = self.__alpha[key]
return instance
else:
raise ValueError('METHOD did not have an appropriate value')
def __iter__(self):
return iter(self.__value)
def main():
x = String('Hello, world!')
y = x[1:]
print(y)
if __name__ == '__main__':
main()
Pass another argument to the constructor, like so:
def __init__(self, string, simple = None):
if simple is None:
self.__string = tuple(string.split())
self.__simple = tuple(self.__simple())
else:
self.__string = string
self.__simple = simple
You can then call it like this:
def __getitem__(self, key):
assert isinstance(key, slice)
return String(self.__string[key], self.__simple[key])
Also, I'm not sure it's allowed to name both the field and the method __simple. If only for readability, you should change that.

Categories