我使用PHP 5.3,它引入了闭包.因为我现在在我的应用程序(和框架)中都有可用的闭包,所以我使用is_callable来查看是什么类型的处理程序$callback.
如果$callback is_callable,我足够了解并使用该函数/方法/闭包.如果它不可调用并且它是一个字符串,那么它可能是$this类中方法的名称.它可能存在,也可能不存在.如果我让它升级到PHP,它将"抛出"一个很好的致命错误(我喜欢那些).
但我想使用mix-ins,这意味着我需要魔术方法__call.__call非常酷,因为它(可以)在实际函数调用之前包含(s)逻辑.但是 ......如果在__call被叫方法不存在之后会发生什么?当然,我可以抛出一个例外,但直到之后我们才会知道.
问题现在是有用的is_callable,因为在实现之后__call,一切都将返回true,因为一切都有回退(存在__call).
有没有办法同时拥有:动态方法和有用的方法is_callable?
我希望看到的是某种可缓存的魔术方法__is_callable,PHP将在is_callable(array($object, $method))调用时"咨询" .
在php.net上,我找不到任何像我这样对此感到愚蠢的人.这不可能!如果你使用__call,你就不is_callable能再使用了!?
我有意义吗?
PS.我已经研究过了,method_exists但这还不够好(即使我可以过滤掉所有的闭包和全局函数),因为它将为私有和受保护的方法以及公共方法返回true.
编辑
我制作了一个'更好'的is_callable来检查混音,但我认为它非常昂贵.
背景:对于那些不熟悉它的人, Sublime Text(和TextMate)通过由.tmLanguage语言定义文件定义的范围提供语法高亮和其他功能,基本上是一组用于识别给定语言中各种结构的正则表达式,例如函数定义,各种类型的字符串,保留字等
我是Python改进包的维护者(如果你感兴趣,可以通过Package Control获得),它旨在成为Python的更好的语言定义.如果需要,您可以在GitHub上阅读它,但其中一个关键特性是它实际上是维护的,这与许多未经过多年更改或更新的Sublime语言不同.
问题:我最近一直关注双重强调的__magic__东西,在找到Rafe Kettler关于魔术函数的优秀论文之后,我能够扩展语言定义的那一部分.但是,我找到一个很好的内置魔术变量名称或魔术属性列表,比如__class__或者运气少了__doc__.我已经浏览了文档的数据模型部分,但它为我的目的留下了一些不足之处,似乎主要关注魔术方法名称.
所以我的问题是,support.variable.magic.python范围应该包含哪些内容?这是它到目前为止的定义:
\b__(all|bases|class|debug|dict|doc|file|members|metaclass|methods|module|name|slots|weakref)__\b
Run Code Online (Sandbox Code Playgroud)
我开始这个项目的原因之一就是自学了更多关于Python的知识,到目前为止我确实取得了成功,但是我有点陷入困境.
为了清楚起见,我不是在寻找一个最喜欢的场外资源(尽管如果你有一个方便的链接,我会很感激)并且我不是要开始一个自以为是的讨论.我想弄清楚的是,这个列表是否看起来是合理的,或者是否有任何明显的错误.如果你确实想要自以为是,请打开一个问题,我非常乐意与你讨论.
谢谢!
我的情况最好用一些代码来描述:
class Foo {
function bar () {
echo "called Foo::bar()";
}
}
class SubFoo extends Foo {
function __call($func) {
if ($func == "bar") {
echo "intercepted bar()!";
}
}
}
$subFoo = new SubFoo();
// what actually happens:
$subFoo->bar(); // "called Foo:bar()"
// what would be nice:
$subFoo->bar(); // "intercepted bar()!"
Run Code Online (Sandbox Code Playgroud)
我知道我可以通过bar()在子类中重新定义(以及所有其他相关方法)来实现这一点,但就我的目的而言,如果__call函数可以处理它们会很好.它会只是让事情很多整洁和更易于管理.
这可能在PHP?
在PHP中,我可以这样做:
class MyClass
{
function __call($name, $args)
{
print('you tried to call a the method named: ' . $name);
}
}
$Obj = new MyClass();
$Obj->nonexistant_method(); // prints "you tried to call a method named: nonexistant_method"
Run Code Online (Sandbox Code Playgroud)
这对于我正在处理的项目能够用Python来做是很方便的(要解析许多讨厌的XML,将它转换为对象并且能够只调用方法会很好.
Python有相同的功能吗?
我有接受dicts或其他对象的方法以及从这些对象中获取的"fields"的名称.如果对象是dict,则该方法用于__getitem__检索命名键,或者getattr用于检索命名属性.这在网络模板语言中非常常见.例如,在变色龙模板中,您可能具有:
<p tal:content="foo.keyname">Stuff goes here</p>
Run Code Online (Sandbox Code Playgroud)
如果你foo像dict一样传入{'keyname':'bar'},然后foo.keyname获取'keyname'键以获得'bar'.如果foo是类的实例,如:
class Foo(object):
keyname = 'baz'
Run Code Online (Sandbox Code Playgroud)
然后foo.keyname从keyname属性中获取值.变色龙本身实现了这个功能(在chameleon.py26模块中),如下所示:
def lookup_attr(obj, key):
try:
return getattr(obj, key)
except AttributeError as exc:
try:
get = obj.__getitem__
except AttributeError:
raise exc
try:
return get(key)
except KeyError:
raise exc
Run Code Online (Sandbox Code Playgroud)
我在自己的包中实现了它,如:
try:
value = obj[attribute]
except (KeyError, TypeError):
value = getattr(obj, attribute)
Run Code Online (Sandbox Code Playgroud)
问题是,这是一种非常常见的模式.我已经在很多模块中看到过这种方法或者与它非常类似的方法.那么为什么在语言的核心,或者至少在一个核心模块中不是这样的东西呢?如果做不到这一点,是那里怎么会以明确的方式应该写?
我有一个使用魔术方法来存储属性的类.这是一个简化的例子:
class Foo {
protected $props;
public function __construct(array $props = array()) {
$this->props = $props;
}
public function __get($prop) {
return $this->props[$prop];
}
public function __set($prop, $val) {
$this->props[$prop] = $val;
}
}
Run Code Online (Sandbox Code Playgroud)
我正在尝试为PDOStatement它的每个数据库行实例化此类的对象,就像这样(不起作用):
$st->setFetchMode(PDO::FETCH_CLASS, 'Foo');
foreach ($st as $row) {
var_dump($row);
}
Run Code Online (Sandbox Code Playgroud)
问题是,在设置属性值时,我的类PDO::FETCH_CLASS似乎没有触发魔术__set()方法.
如何使用PDO实现所需效果?
在尝试包装任意对象时,我遇到了字典和列表的问题.调查,我设法提出了一段简单的代码,其行为我根本不理解.我希望你们中的一些人可以告诉我发生了什么:
>>> class Cl(object): # simple class that prints (and suppresses) each attribute lookup
... def __getattribute__(self, name):
... print 'Access:', name
...
>>> i = Cl() # instance of class
>>> i.test # test that __getattribute__ override works
Access: test
>>> i.__getitem__ # test that it works for special functions, too
Access: __getitem__
>>> i['foo'] # but why doesn't this work?
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'Cl' object has no attribute '__getitem__'
Run Code Online (Sandbox Code Playgroud) 我不确定标题应该是什么,但代码应该更好地解释它:
class Group {
private $number = 20;
public function __toString() {
return "$this->number";
}
}
$number = new Group();
echo $number, PHP_EOL;
echo ++ $number, PHP_EOL;
echo PHP_EOL;
$number = "20";
echo $number, PHP_EOL;
echo ++ $number, PHP_EOL;
echo PHP_EOL;
$number = 20;
echo $number, PHP_EOL;
echo ++ $number, PHP_EOL;
Run Code Online (Sandbox Code Playgroud)
输出:
20
20 <--- Expected 21
20
21
20
21
Run Code Online (Sandbox Code Playgroud)
知道我为什么20而不是21?即使这样,下面的代码也适用:
$i = null ;
echo ++$i ; // output 1
Run Code Online (Sandbox Code Playgroud)
我知道Group是一个实现的对象__toString …
在重构之后,我们在其中一个类中有类似的东西:
class FooBar
{
// $foo was $bla before
private $foo;
public function setBlubbOnArrayOnlyOnce($value)
{
// $this->bla was forgotten during refactoring. Must be $this->foo
if(!isset($this->bla['blubb'])) {
$this->foo['blubb'] = $value;
}
}
}
Run Code Online (Sandbox Code Playgroud)
所以最后$ this-> foo ['blubb']总是被设定,不仅一次.这是因为PHP的神奇方法.我们不希望它可以动态访问字段,所以我想我只是添加了一个代码不一致的规则.但我没有发现任何问题,并问我原因.
PHPStorm显示了一个动态通知的字段,但我想在部署周期中使用代码(或类似的东西)自动失败.
有人对此有所了解吗?有一个很好的规则吗?我应该写自己的,怎么样?或者禁用它是不好的做法?
免责声明:我们使用测试,但有时你会错过一些东西......首先要防止这种情况.另外,请不要想出覆盖魔法的方法.我不想在每个班级都有任何特质/抽象.
magic-methods ×10
php ×6
python ×5
built-in ×1
call ×1
codesniffer ×1
getattr ×1
increment ×1
oop ×1
overloading ×1
overriding ×1
pdo ×1
sublimetext ×1
syntax ×1