How to Have Class.Property = X Return Something Other Than X

Is it possible to have class.property = x return something other than x?

One downside is that you would break the chained assignment semantics:

$ irb 
irb(main):001:0> x = y = 3
=> 3
irb(main):002:0> p x
3
=> nil
irb(main):003:0> p y
3
=> nil
irb(main):004:0>

Consider:

x = MyClass.property = 3

Then x would take true if this worked as you had expected (right-associativity). That could be a surprise for people using your interface and used to the typical semantics.

You also got me thinking about parallel assignment, eg:

x, y = 1, 2

Apparently the return value from that expression is implementation specific... I guess I won't be chaining parallel assignments :)

Nice question!

Is it possible to have a function return a class instead of an object?

I was surprised that I couldn't make this work with the constructor directly, but you can at least return a delegate/lambda expression that calls into the constructor you want:

Class TestFactory
Public Shared Function Create(byval aParam as Integer) as Func(Of Integer, Test)
Select aParam
Case 0
return Function(id) New Test_A(id)
Case 1
Return Function(id) New Test_B(id)
...
End Select
End Function
End Class

And then you can call it almost exactly as you described:

Dim aTest As Test = TestFactory.Create(0)(100)

What we are doing now is effectively Currying your Test factory.

My experience, though, is that this is the wrong direction in the first place. Create() methods generally return instances, not Types, and right or wrong, there will be an expectation for this among other programmers who use the type and are familiar with the pattern. If you do go down this path, at least consider a different name for the method.

Is there any way to create a class property in Python?

If you want the descriptor property to trigger when you get an attribute from object X, then you must put the descriptor in type(X). So if X is a class, the descriptor must go in the class's type, also known as the class's metaclass -- no "trickery" involved, it's just a matter of completely general rules.

Alternatively, you might write your own special-purpose descriptor. See here for an excellent "how-to" treaty on descriptors. Edit for example:

class classprop(object):
def __init__(self, f):
self.f = classmethod(f)
def __get__(self, *a):
return self.f.__get__(*a)()

class buh(object):
@classprop
def bah(cls): return 23

print buh.bah

emits 23, as desired.

How to implement __iadd__ for a Python property

__iadd__ will only be looked for on the value returned from __get__. You need to make __get__ (or the property getter) return an object (or a proxy object) with __iadd__.

@property
def x(self):
proxy = IProxy(self._x)
proxy.parent = self
return proxy

class IProxy(int, object):
def __iadd__(self, val):
self.parent.iadd_x(val)
return self.parent.x

Storing functions in C# class properties?

You want Func<double, double> instead of Action; given double (e.g. 4.5 Volt) return double.

x => x / 1000

On the contrary, Action takes no arguments and returns nothing: () => {...}

Code:

// Let's implement immutable class (assigned once, never change)
// in order to prevent occasional errors like device.Name = ...
private class Device {
public string Name { get; }
public Func<double, double> Conversion { get; }
public string Unit { get; }

// Let's validate the input (at least, for null)
public Device(string name, Func<double, double> conversion, string unit) {
if (null == name)
throw new ArgumentNullException(nameof(name));
else if (null == conversion)
throw new ArgumentNullException(nameof(conversion));
else if (null == unit)
throw new ArgumentNullException(nameof(unit));

Name = name;
Conversion = conversion;
Unit = unit;
}
}

...

List<Device> deviceList = new List<Device>() {
new Device("battery-voltage", x => x / 1000, "V"),
new Device("air-temperature", x => (175 * x / 65535) - 45, "°C"),
};

Possible usage:

// What device should we use if we want °C unit?
Device temperature = deviceList
.FirstOrDefault(item => item.Unit == "°C");

byte[] dataToConvert = new byte[] {123, 45, 79};

// Device found
if (temperature != null) {
// Converting: for each value in dataToConvert we obtain corresponding t value
foreach (var value in dataToConvert) {
double t = temperature.Conversion(value);
...
}
}

Or you can even have an array of converted values (double[]) with a help of Linq:

byte[] dataToConvert = new byte[] {123, 45, 79};

// Let's throw exception if device has not been found
Device temperature = deviceList
.First(item => item.Unit == "°C");

double[] results = dataToConvert
.Select(v => temperature.Convert(v))
.ToArray();


Related Topics



Leave a reply



Submit