访问模块外部的模块属性

She*_*yar 16 elixir

我在elixir中有一个属性的模块:

defmodule MyAwesomeModule do
  @awesome_number 7

  # other stuff...
end
Run Code Online (Sandbox Code Playgroud)

我无法访问@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
Run Code Online (Sandbox Code Playgroud)

所以现在,我将模块属性包装在一个方法中来访问它,但它对我来说并没有多大意义.我可以简单地使用该方法并停止一起使用该属性:

defmodule MyAwesomeModule do
  @awesome_number 7

  def awesome_number, do: @awesome_number

  # other stuff...
end
Run Code Online (Sandbox Code Playgroud)

所以我的问题是,有更好/正确的方法吗?

ven*_*laf 19

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
Run Code Online (Sandbox Code Playgroud)

请注意,读取函数内的属性会获取其当前值的快照.换句话说,该值在编译时读取,而不是在运行时读取.正如我们将要看到的,这使得属性可用于在模块编译期间用作存储.

将它们与Module.register_attribute/3(https://hexdocs.pm/elixir/Module.html#register_attribute/3)一起使用,尤其是accumulate: true选项,使它们在更多方面有用.

我想说的是,它们比仅仅用作常量更有用.

  • Elixir的Plug库是使用`accumulate:true`模块属性的一个很好的例子.如果你想在源代码中挖掘更多信息,请点击这里链接https://github.com/elixir-lang/plug/blob/19f53a67e672152a7393611681431c1e0ec1be04/lib/plug/builder.ex#L121 (4认同)

Dav*_*rsa 10

有一种通过使用use和宏来"欺骗"的方法.看看这个例子.

例如,假设您将模块定义为:

defmodule AwesomeLibrary do  
  defmacro __using__(_) do
    quote do
      def print(s), do: IO.puts(s)
    end
  end
end 
Run Code Online (Sandbox Code Playgroud)

然后,在某些模块中,您可以通过use这种方式使用关键字.

defmodule TestLibrary do  
  use AwesomeLibrary
end
Run Code Online (Sandbox Code Playgroud)

结果是__using__块中定义的所有内容都在编译时复制到新模块中.因此,在这种情况下,TestLibrary.print即使print在另一个模块中定义,也可以使用.

这也用于复制常量.作为示例,您可以查看TimeX库.它使用专用模块来处理需要时导入的常量.

在我看来,这是在大型代码库中共享常量定义的更好方法.

  • 在TimeX中,这是一个很好的链接。这也是我使用它们的方式。我创建了一个概念应用程序,以研究跨项目共享“常量”的不同方式:https://github.com/bill-mybiz/elixir-constants-concept。我还针对常量创建了一个SO“文档”(现在为“复习”),因为这是elixir的文档不那么强的少数领域之一。(关于其他所有内容的记录都很好)。这种方法的另一个好处是,您可以在保护语句中使用它们(因为它是编译时)。 (3认同)