Get the Value of an Instance Variable Given Its Name

Get the value of an instance variable given its name

The most idiomatic way to achieve this is:

some_object.instance_variable_get("@#{name}")

There is no need to use + or intern; Ruby will handle this just fine. However, if you find yourself reaching into another object and pulling out its ivar, there's a reasonably good chance that you have broken encapsulation.

If you explicitly want to access an ivar, the right thing to do is to make it an accessor. Consider the following:

class Computer
def new(cpus)
@cpus = cpus
end
end

In this case, if you did Computer.new, you would be forced to use instance_variable_get to get at @cpus. But if you're doing this, you probably mean for @cpus to be public. What you should do is:

class Computer
attr_reader :cpus
end

Now you can do Computer.new(4).cpus.

Note that you can reopen any existing class and make a private ivar into a reader. Since an accessor is just a method, you can do Computer.new(4).send(var_that_evaluates_to_cpus)

Get instance variable by its name

This is more or less what hashmaps were made for, for pairing a key (in this case a string) to an object

class B
{
private HashMap<String,A> theAs=new HashMap<String,A>(); //give better name


public B ()
{
theAs.put("a1",new A ("a1")); //A probably doesn't need to keep its own name internally now, but have left it as its in your original code
theAs.put("a2",new A ("a2"));
theAs.put("a3",new A ("a3"));
}

public A get_A_byName (String name)
{
return theAs.get(name);
}
}

Notes

  • In Java 7+ you don't need to add the second <String,A>, diamond
    inference will do, so in Java 7 or higher it would be:

    private HashMap<String,A> theAs=new HashMap<>();

How to access instance variable from its string name ?

Use instance_variable_set and instance_variable_get. Keep in mind the string needs to have the leading @:

@foo = "bar"
# => "bar"
instance_variable_get("@foo")
# => "bar"
instance_variable_set("@foo", "baz")
# => "baz"
@foo
# => "baz"

Access instance variable by using instance name

If by "call" you actually mean call and not just "access" you can simply implement a __call__ method:

class PropertyExpr:
def __init__(self, value, expr):
self.value = value
self.expr = expr

def __call__(self):
return self.value

prop1 = PropertyExpr(5, "test")

val = prop1()
print(val)

Output:

5

In that case, the result of calling prop1() can be really anything.

Other than that, what you want is not possible. You could override the __new__ method, but that will also change the type of what you're creating. So if you're returning 5 your object will be 5, but it will also be an int and no longer an instance of PropertyExpr and all your additional attributes will be lost:

class PropertyExpr():
def __new__(cls, value, expr):
return value

def __init__(self, value, expr):
self.value = value
self.expr = expr

prop1 = PropertyExpr(5, "test")

print(prop1, type(prop1))
try:
print(prop1.expr)
except Exception as e:
print(e)

Output:

5 <class 'int'>
'int' object has no attribute 'expr'

After some trying around, I've figured out a way to dynamically change the type of the constructor, however I would advise against actually using this:

class PropertyExpr:
def __new__(cls, tp, value, *args, **kwargs):
return type("FakeType", (tp,), kwargs)(value)

prop1 = PropertyExpr(int, 5, expr="expr int")
print(prop1, " -> ", prop1.expr)

prop2 = PropertyExpr(list, "5", expr="expr list")
print(prop2, " -> ", prop2.expr)

prop3 = PropertyExpr(str, "abc", expr="expr string")
print(prop3, " -> ", prop3.expr)

Output:

5  ->  expr int     
['5'] -> expr list
abc -> expr string

You can pass the type you wish to sub-class as the first parameter, the second parameter should be a value accepted by the type's contructor and then you can pass in arbitrary kwargs that will be added as attributes to the created object.



is there a way to make is so that type(prop1) would still be PropertyExpr? Edit: for example, if we can do isinstance(prop1, PropertyExpr) = True then everything would be perfect

I could not get that to work (that does not mean that others cannot), but I managed to make it work with multi inheritance, so you can use isinstance(prop1, PropertyExprMixin):

class PropertyExprMixin:
pass

class PropertyExpr:
def __new__(cls, tp, value, *args, **kwargs):
return type("PropertyExprMixin", (tp,PropertyExprMixin), kwargs)(value)

prop1 = PropertyExpr(int, 5, expr="expr int")
print(prop1, " -> ", prop1.expr, type(prop1), isinstance(prop1, int), isinstance(prop1, PropertyExprMixin))

prop2 = PropertyExpr(list, "5", expr="expr list")
print(prop2, " -> ", prop2.expr, type(prop2), isinstance(prop2, list), isinstance(prop2, PropertyExprMixin))

prop3 = PropertyExpr(str, "abc", expr="expr string")
print(prop3, " -> ", prop3.expr, type(prop3), isinstance(prop3, str), isinstance(prop3, PropertyExprMixin))

Output:

5  ->  expr int <class '__main__.PropertyExprMixin'> True True     
['5'] -> expr list <class '__main__.PropertyExprMixin'> True True
abc -> expr string <class '__main__.PropertyExprMixin'> True True

Retrieve specific values of instance variable from each object of a list containing Objects of different classes

Use Reflection

Assuming the classes do not share a common base class or interface which contains the method you may use Reflection to invoke the method. There is a lot of error handling to make it work properly but the following example waves that away and forces the caller to deal with it.

public String getName(Object named)
throws NoSuchMethodException,
SecurityException,
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
{
Method method = named.getClass().getMethod("getName");
return (String) method.invoke(named);
}

The following iterates a List of objects and invokes the above method. Since the Exceptions are percolating up, this method has to handle them. You could just catch Exception here but I left the full list for completeness.

public void printNames(List<Object> namedObjects) {
for (Object obj : namedObjects) {
try {
System.out.println(getName(obj));
} catch (NoSuchMethodException |
SecurityException |
IllegalAccessException |
IllegalArgumentException |
InvocationTargetException e) {
System.err.println("Failed to invoke getName on object - skipping");
}
}
}

Getting the value of a variable based on a string name in Java

You can use reflection and a helper method for this:

public static Object getFieldValue(Object target, String fieldName){
// get field from class
Field field = target.getClass().getDeclaredField(fieldName);

// set accessability to "public"
field.setAccessible(true);
try{
// get value
return field.get(target);
} catch(IllegalAccessException e){
// or throw error
throw new IllegalStateException(e);
}
}

This can then be invoked easily with:

Object value = getFieldValue(this, "sample");

The snippet uses Class#getDeclaredField(String) as suggested by @aszro in the comments. This method returns the field by name or throws a NoSuchElementException if no field exists.

Access instance variable using its name (as symbol)

Here are ways to do both techniques. Assuming we already have your class definition,

position = Position.new(1, 2)
axis = :x
position.send axis #=> 1
axis = :y
position.send axis #=> 2

The Object#send method accepts at least a symbol representing the name of the method to call, and call it. You can also pass arguments to the method after the name and a block, too.

The second way to do this (using your Position#get method) is

class Position
def get(axis)
send axis
end
end

position = Position.new(1, 2)
axis = :x
position.get axis #=> 1
axis = :y
position.get axis #=> 2

I recommend this way because it encapsulates the technique for getting the values. Should you need to change it later, you don't need to change all the code that uses Position.

Get instance variable by name in Swift

From the Swift docs (emphasis mine):

If you have experience with Objective-C, you may know that it provides two ways to store values and references as part of a class instance. In addition to properties, you can use instance variables as a backing store for the values stored in a property.

Swift unifies these concepts into a single property declaration. A Swift property does not have a corresponding instance variable, and the backing store for a property is not accessed directly. This approach avoids confusion about how the value is accessed in different contexts and simplifies the property’s declaration into a single, definitive statement. All information about the property—including its name, type, and memory management characteristics—is defined in a single location as part of the type’s definition.

Depending on how pointerOfIvarForPropertyNamed is used, you may not be able to literally translate that method into Swift, since it doesn't have Ivars. You may be able to use Key-Value Coding to achieve what you want.

If you want to expand on how this method is used, I can expand my answer appropriately.



Related Topics



Leave a reply



Submit