How to Dynamically Create a Local Variable

How to dynamically create a local variable?

You cannot dynamically create local variables in Ruby 1.9+ (you could in Ruby 1.8 via eval):

eval 'foo = "bar"'
foo # NameError: undefined local variable or method `foo' for main:Object

They can be used within the eval-ed code itself, though:

eval 'foo = "bar"; foo + "baz"'
#=> "barbaz"

Ruby 2.1 added local_variable_set, but that cannot create new local variables either:

binding.local_variable_set :foo, 'bar'
foo # NameError: undefined local variable or method `foo' for main:Object

This behavior cannot be changed without modifying Ruby itself. The alternative is to instead consider storing your data within another data structure, e.g. a Hash, instead of many local variables:

hash = {}
hash[:my_var] = :foo

Note that both eval and local_variable_set do allow reassigning an existing local variable:

foo = nil
eval 'foo = "bar"'
foo #=> "bar"
binding.local_variable_set :foo, 'baz'
foo #=> "baz"

How to dynamically create local and global variables?

You cannot define a local variable with a full stop (.) character in ruby. That is not valid syntax.

(eval):2: unexpected fraction part after numeric literal
string_1.0 = "1.0"

Additionally, you cannot dynamically define local variables. There are various workarounds to sort-of achieve this, however, fundamentally I think you are asking an XY problem.

For example, have you considered using an OpenStruct, or passing this hash as locales when rendering a template, or instead dynamically setting instance variables?

Dynamically set local variable

Contrary to other answers already posted you cannot modify locals() directly and expect it to work.

>>> def foo():
lcl = locals()
lcl['xyz'] = 42
print(xyz)


>>> foo()

Traceback (most recent call last):
File "<pyshell#6>", line 1, in <module>
foo()
File "<pyshell#5>", line 4, in foo
print(xyz)
NameError: global name 'xyz' is not defined

Modifying locals() is undefined. Outside a function when locals() and globals() are the same it will work; inside a function it will usually not work.

Use a dictionary, or set an attribute on an object:

d = {}
d['xyz'] = 42
print(d['xyz'])

or if you prefer, use a class:

class C: pass

obj = C()
setattr(obj, 'xyz', 42)
print(obj.xyz)

Edit:
Access to variables in namespaces that aren't functions (so modules, class definitions, instances) are usually done by dictionary lookups (as Sven points out in the comments there are exceptions, for example classes that define __slots__). Function locals can be optimised for speed because the compiler (usually) knows all the names in advance, so there isn't a dictionary until you call locals().

In the C implementation of Python locals() (called from inside a function) creates an ordinary dictionary initialised from the current values of the local variables. Within each function any number of calls to locals() will return the same dictionary, but every call to locals() will update it with the current values of the local variables. This can give the impression that assignment to elements of the dictionary are ignored (I originally wrote that this was the case). Modifications to existing keys within the dictionary returned from locals() therefore only last until the next call to locals() in the same scope.

In IronPython things work a bit differently. Any function that calls locals() inside it uses a dictionary for its local variables so assignments to local variables change the dictionary and assignments to the dictionary change the variables BUT that's only if you explicitly call locals() under that name. If you bind a different name to the locals function in IronPython then calling it gives you the local variables for the scope where the name was bound and there's no way to access the function locals through it:

>>> def foo():
... abc = 123
... lcl = zzz()
... lcl['abc'] = 456
... deF = 789
... print(abc)
... print(zzz())
... print(lcl)
...
>>> zzz =locals
>>> foo()
123
{'__doc__': None, '__builtins__': <module '__builtin__' (built-in)>, 'zzz': <built-in function locals>, 'foo': <function foo at 0x000000000000002B>, '__name__': '__main__', 'abc': 456}
{'__doc__': None, '__builtins__': <module '__builtin__' (built-in)>, 'zzz': <built-in function locals>, 'foo': <function foo at 0x000000000000002B>, '__name__': '__main__', 'abc': 456}
>>>

This could all change at any time. The only thing guaranteed is that you cannot depend on the results of assigning to the dictionary returned by locals().

Is it possible to dynamically create local variables in Python?

If you really want to do this, you could use exec:

print 'iWantAVariableWithThisName' in locals()
junkVar = 'iWantAVariableWithThisName'
exec(junkVar + " = 1")
print 'iWantAVariableWithThisName' in locals()

Of course, anyone will tell you how dangerous and hackish using exec is, but then so will be any implementation of this "trickery."

How to create dynamic global and local variables?

Take this answer with a grain of salt because I'm no expert on Ruby's eval, but this worked for me:

require 'json'

hash = '{"$a":5,"b":10}'
hash1 = JSON.parse(hash)
bind = binding
hash1.each do |k,v|
# Dunno what this is for and things work fine without it.
# singleton_class.send(:attr_accessor,k)

# You don't want to call `send` here, just call `eval` directly.
eval("#{k}=#{v}", bind)
end

puts $a
# => 5

puts b
# => 10

Note that I'm passing in a binding from the parent scope to eval since you'll want to access local variables such as b in the parent scope after you exit the loop.

Lastly, v needed to be part of the string passed to eval, not the second parameter passed to eval, which, if anything, needs to be a binding. If you pass an integer it will raise an exception.

However, I should warn you that global variables should be used with restraint, and that using eval can be dangerous, especially if you get this JSON from someone else.

How to dynamically create local variables in main

You could define instance variables dynamically :

5.times do |i|
instance_variable_set(:"@reader#{i}", "library_name#{i}")
instance_variable_set(:"@book#{i}", "book_title#{i}")
end

puts @reader1
puts @book1
puts @book4

# => library_name1
# book_title1
# book_title4

Another possibility would be to use method_missing to fake local variables, while using instance variables as cache :

def create_variable_or_use_cache(name, &block)
name = "@#{name}"
instance_variable_get(name) || instance_variable_set(name, block.yield)
end

def method_missing(sym,*p)
if sym=~/^reader(\d+)$/ then
create_variable_or_use_cache(sym){ "Create reader#{$1} here" }
elsif sym=~/^book(\d+)$/ then
create_variable_or_use_cache(sym){ "Create book#{$1} here" }
else
super
end
end

puts reader1
puts reader1
puts book3
wrong_method

# =>
# Create reader1 here
# Create reader1 here
# Create book3 here
# binding.rb:13:in `method_missing': undefined local variable or method `wrong_method' for main:Object (NameError)

It's an interesting Ruby exercise, I'm not sure you should use it though.

How do I dynamically create a local variable in Ruby?

You have to use the correct binding. In IRB for example this would work:

irb(main):001:0> eval "t=2", IRB.conf[:MAIN_CONTEXT].workspace.binding
=> 2
irb(main):002:0> local_variables
=> [:t, :_]
irb(main):003:0> eval "t"
=> 2
irb(main):004:0> t
=> 2

Terraform Creating map dynamically using local variable instead of input variable

i can help you create a dynamic list of maps with terraform using a hack here's an example :

locals {
childs = 24
group = [1,2,3,4,5,6]
}

# Here's the hack! The null_resource has a map called triggers that we can set to arbitrary values.
# We can also use count to create a list of null_resources. By accessing the triggers map inside of
# that list, we get our list of maps! See the output variable below.
resource "null_resource" "res" {
count = local.childs+1

triggers = {
parent = "${count.index}"
child = "${element(local.group, count.index)}"
}
}

# And here's the result! We have a dynamic list of maps. I'm just outputting it here

output "map" {
value = "${null_resource.res.*.triggers}"
}

you can try it create a main.tf and run terraform init terraform apply

the result should be like this :

map = [
{
"child" = "1"
"parent" = "0"
},
{
"child" = "2"
"parent" = "1"
},
{
"child" = "3"
"parent" = "2"
},
{
"child" = "4"
"parent" = "3"
},
{
"child" = "5"
"parent" = "4"
},
{
"child" = "6"
"parent" = "5"
},
{
"child" = "1"
"parent" = "6"
},
{
"child" = "2"
"parent" = "7"
},
{
"child" = "3"
"parent" = "8"
},
{
"child" = "4"
"parent" = "9"
},
{
"child" = "5"
"parent" = "10"
},
{
"child" = "6"
"parent" = "11"
},
{
"child" = "1"
"parent" = "12"
},
{
"child" = "2"
"parent" = "13"
},
{
"child" = "3"
"parent" = "14"
},
{
"child" = "4"
"parent" = "15"
},
{
"child" = "5"
"parent" = "16"
},
{
"child" = "6"
"parent" = "17"
},
{
"child" = "1"
"parent" = "18"
},
{
"child" = "2"
"parent" = "19"
},
{
"child" = "3"
"parent" = "20"
},
{
"child" = "4"
"parent" = "21"
},
{
"child" = "5"
"parent" = "22"
},
{
"child" = "6"
"parent" = "23"
},
{
"child" = "1"
"parent" = "24"
},
]

If you want to check every parent and every child (you can use locals)
you can create a 2 loops like this :

   locals {
childs = 24
group = 6

result = {
for j in range(1, local.childs + 1) : j => [
for i in range(1, local.group + 1) : {
child = i,
parent = j
}
]

}
}

your output will be grouped by parents like this :

  "1" = [
{
"child" = 1
"parent" = 1
},
{
"child" = 2
"parent" = 1
},
{
"child" = 3
"parent" = 1
},
{
"child" = 4
"parent" = 1
},
{
"child" = 5
"parent" = 1
},
{
"child" = 6
"parent" = 1
},
]
"2" = [
{
"child" = 1
"parent" = 2
},
{
"child" = 2
"parent" = 2
},
{
"child" = 3
"parent" = 2
},
{
"child" = 4
"parent" = 2
},
{
"child" = 5
"parent" = 2
},
{
"child" = 6
"parent" = 2
},
]


Related Topics



Leave a reply



Submit