在mathematica包中定义私有函数
我不确定我是否正确定义了私有函数。 当我编写一个数学包时,我只是这样做:
BeginPackage["myPackage`"]
myPublicFunction::usage="myPublicFunction blahblahblah";
Begin["Private"]
myPrivateFunction[input_]:= ... ;
myPublicFunction[input_]:= ... ;
End[]
EndPackage[]
这是正确的方法还是我遗漏了一些东西?
I'm not sure I got how to define private functions right.
When I'm writing a package mathematica, I just do this:
BeginPackage["myPackage`"]
myPublicFunction::usage="myPublicFunction blahblahblah";
Begin["Private"]
myPrivateFunction[input_]:= ... ;
myPublicFunction[input_]:= ... ;
End[]
EndPackage[]
Is this the correct way or am I missing something?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
是的,这是正确的方法。了解一些内部包机制可能会有所帮助。 Mathematica 上下文与其他语言中的命名空间类似。它们可以嵌套。每个符号都属于某个上下文。在任何给定时刻,某些上下文都是“当前的”。每当创建新符号时,系统必须决定该符号属于哪个上下文。这发生在解析时。这里的基本量(变量)是
$ContextPath
。它基本上是符号的搜索路径。它是一个上下文列表,每当系统看到一个新符号时,它都会测试具有相同短名称的符号(即符号本身的名称,没有上下文)是否存在于上的某个上下文中$ContextPath
。如果确实存在,则该符号的给定出现将与该现有符号相关联。如果没有,则在当前上下文中创建该符号。请注意,这是一个动态的事情 - 如果您随时更改$ContextPath
,则下一个符号出现可能会与不同的符号关联。无论如何,
BeginPackage
所做的只是用{youPublicPackageContext, "System'"}
以及您公开导入的可能的其他上下文替换 $ContextPath 的当前值BeginPackage
的第二个可选参数。因此,如果“公共”部分中的所有符号不在“系统”或您导入的其他上下文中,则它们都会被解析到公共上下文中。EndPackage
的作用是将$ContextPath
的值恢复到开始加载包之前的值。因此,从技术上讲,使用消息并不是在主上下文中公开符号的唯一方法 - 您也可以简单地键入带有分号的符号,例如myFunction;
(不鼓励这种做法,我只是提到它来澄清机制)。现在,当您输入Begin["'Private'"]
时,当前上下文将变为YourContext'Private'
(子上下文)。$ContextPath
未更改。因此,在那里输入的任何符号,如果您的公共包或其他导入的包(即当前位于$ContextPath
上的上下文)中不存在,都会自动解析为'Private'< /代码> 子上下文。
真正使这些符号成为私有的原因是,每当您将包导入到其他上下文(包)时,只有主包会添加到
$ContextPath
中,而不是其子包。从技术上讲,您可以通过手动将YourPackage'Private'
添加到 $ContextPath(例如PrependTo[$ContextPath, YourPackage'Private']
)来打破封装,然后将所有私有函数和其他符号将在您执行导入的特定上下文中变为公共。同样,不鼓励这种做法,但它解释了机制。最重要的是,当我们知道如何解析符号,以及$ContextPath
和$Context
(另一个系统变量给出当前上下文的值),由Begin
和BeginPackage
等命令执行。换句话说,原则上可以模拟BeginPackage
、Begin
、End
和EndPackage
的操作> 使用用户定义的代码。这里只有一些操作原则(我试图在上面概述),并且该机制本身实际上非常暴露给用户,因此,如果在某些罕见的情况下,人们可能想要一些其他行为,则可以使使用$ContextPath
和Context
进行一些“自定义”操作,以确保某种非标准的符号解析方式,从而控制某些“非标准”中的包规模封装方式。我并不是鼓励这样做,只是想强调这个机制实际上比表面上看起来更简单、更可控。Yep, that's a correct way. It may pay off to understand some of the internal package mechanics. Mathematica contexts are similar to namespaces in other languages. They can be nested. Every symbol belongs to some context. At any given moment, some context is "current". Whenever a new symbol is created, the system must decide to which context the symbol will belong. This happens at parse-time. The fundamental quantity (variable) here is
$ContextPath
. It is basically the search path for symbols. It is a list of contexts, and whenever the system sees a new symbol, it tests if the symbol with the same short name (that is, the name of the symbol proper, without the context) exists in some context on the$ContextPath
. If it does exist, then the given occurrence of the symbol will be associated with that existing one. If it does not, then the symbol is created in a current context. Note that this is a dynamic thing - if you change the$ContextPath
at any moment, the next symbol occurrence can be associated with a different symbol.Anyways, what
BeginPackage
does is that it simply replaces the current value of $ContextPath with just{youPublicPackageContext, "System'"}
, plus possibly additional contexts that you publicly import through the second optional argument ofBeginPackage
. Therefore, all symbols that are in the "public" section, are parsed into the public context, if they are not in "System'" or other contexts that you import. And whatEndPackage
does is to restore the value of the$ContextPath
to what it was before you started loading the package. So, technically the usage message is not the only way to make a symbol public in your main context - you could just as well simply type a symbol with a semicolon, likemyFunction;
(this practice is discouraged, I just mentioned it to clarify the mechanism). Now, what happens when you enterBegin["'Private'"]
is that the current context becomesYourContext'Private'
(a sub-context). The$ContextPath
is not changed. Therefore, any symbol entered there, which does not exist in your public package or other imported packages (that is, contexts currently on the$ContextPath
), automatically is parsed into the'Private'
subcontext.What really makes these symbols private is that whenever you import your package into some other context (package), only the main package is added to the
$ContextPath
, but not its sub-packages. Technically, you can break encapsulation by manually addingYourPackage'Private'
to the $ContextPath (say,PrependTo[$ContextPath, YourPackage'Private']
), and then all your private functions and other symbols will become public in that particular context where you do the import. Again, this practice is discouraged, but it explains the mechanics. The bottom line is that the notion of private or public can be entirely understood when we know how symbols are parsed, and what are the manipulations with$ContextPath
and$Context
(another system variable giving the value of the current context), that are performed by commands such asBegin
andBeginPackage
. To put it another way, one could, in principle, emulate the actions ofBeginPackage
,Begin
,End
andEndPackage
with a user-defined code. There are just a few principles operating here (which I tried to outline above), and the mechanism itself is in fact very much exposed to the user, so that if, in some rare cases, one may want some other behavior, one can make some "custom" manipulations with$ContextPath
andContext
, to ensure some non-standard way of symbol parsing and therefore, control package-scale encapsulation in some "non-standard" way. I am not encouraging this, just mentioning to emphasize that the mechanism is in fact much simpler and much more controllable than it may seem on the surface.