我在elixir中有一个带有属性的模块:
defmodule MyAwesomeModule do
@awesome_number 7
# other stuff...
end
我无法访问模块外的@awesome_number
。我已尝试使用Module.get_attribute/2
方法,但它会抛出此错误:
iex(79)> Module.get_attribute(MyAwesomeModule, :awesome_number)
** (ArgumentError) could not call get_attribute on module MyAwesomeModule because it was already compiled
(elixir) lib/module.ex:1101: Module.assert_not_compiled!/2
(elixir) lib/module.ex:1016: Module.get_attribute/3
所以现在,我将模块属性包装在一个访问它的方法中,但它对我来说并没有多大意义。我可以简单地使用该方法并停止一起使用该属性:
defmodule MyAwesomeModule do
@awesome_number 7
def awesome_number, do: @awesome_number
# other stuff...
end
所以我的问题是,有更好/正确的方法吗?
答案 0 :(得分:18)
AFAIK无法访问给定模块之外的模块属性。定义一个公开模块属性的函数是要走的路,就是你正在做的事情。
仍然有充分的理由保留模块属性,而不是仅使用没有模块属性的函数。这取决于具体情况。请记住,存储在模块属性中的值是在编译时计算的。话虽如此,您可能有不同的理由使用或不使用模块属性。让我们看看以下示例:
如果awesome_number
每次访问时都必须随机生成,则必须使用函数。
如果awesome_number
需要计算(长时间)并且不必更改其值,那么使用模块属性+函数来公开它,是可行的方法。
修改强>
我之前说过的模块属性还有很多。它们的功能优于功能。以下是elixir文档中的示例和引用:
defmodule MyServer do
@my_data 14
def first_data, do: @my_data
@my_data 13
def second_data, do: @my_data
end
MyServer.first_data #=> 14
MyServer.second_data #=> 13
请注意,读取函数内的属性会拍摄快照 它的当前价值。换句话说,在编译时读取该值 时间,而不是在运行时。正如我们将要看到的,这会产生属性 在模块编译期间用作存储空间很有用。
将它们与Module.register_attribute/3
(https://hexdocs.pm/elixir/Module.html#register_attribute/3)一起使用,尤其是accumulate: true
选项,可以在更多方面使用它们。
我想说的是,它们比仅仅用作常量更有用。
答案 1 :(得分:8)
使用use
和宏有一种“作弊”的方法。请看this example。
例如,假设您将模块定义为:
defmodule AwesomeLibrary do
defmacro __using__(_) do
quote do
def print(s), do: IO.puts(s)
end
end
end
然后,在某些模块中,您可以通过这种方式使用use
关键字。
defmodule TestLibrary do
use AwesomeLibrary
end
效果是__using__
块中定义的所有内容都在编译时复制到新模块中。因此,在这种情况下,即使在另一个模块中定义了TestLibrary.print
,也可以使用print
。
这也用于复制常量。作为示例,您可以查看TimeX库。它使用在需要时导入的a dedicated module for constants。
在我看来,这是在大型代码库中共享常量定义的更好方法。