Programming languages 什么是鸭子打字?

Programming languages 什么是鸭子打字?,programming-languages,duck-typing,Programming Languages,Duck Typing,我在网上阅读软件上的随机主题时遇到了duck-typing这个词,并没有完全理解它 什么是“duck typing”?这是一个在中使用的术语,但没有 其思想是,调用对象上的现有方法不需要类型——如果在对象上定义了方法,则可以调用它 这个名字来源于“如果它看起来像鸭子,嘎嘎叫起来像鸭子,那它就是鸭子” 有更多的信息。维基百科有一个相当详细的解释: duck类型是一种动态类型 键入对象的当前 方法和属性集 确定有效的语义,而不是 而不是从某个特定的 类或特定对象的实现 接口 重要的一点是,对于du

我在网上阅读软件上的随机主题时遇到了duck-typing这个词,并没有完全理解它

什么是“duck typing”?

这是一个在中使用的术语,但没有

其思想是,调用对象上的现有方法不需要类型——如果在对象上定义了方法,则可以调用它

这个名字来源于“如果它看起来像鸭子,嘎嘎叫起来像鸭子,那它就是鸭子”


有更多的信息。

维基百科有一个相当详细的解释:

duck类型是一种动态类型 键入对象的当前 方法和属性集 确定有效的语义,而不是 而不是从某个特定的 类或特定对象的实现 接口

重要的一点是,对于duck类型,开发人员可能更关心使用的对象部分,而不是实际的底层类型

鸭型 这意味着一个操作没有正式指定其操作数必须满足的要求,而只是用给定的内容进行试验

与其他人所说的不同,这不一定与动态语言或继承问题有关

示例任务:调用对象上的某个方法
Quack

在不使用duck类型的情况下,执行此任务的函数
f
必须事先指定其参数必须支持某种方法
Quack
。一种常见的方法是使用接口

interface IQuack { 
    void Quack();
}

void f(IQuack x) { 
    x.Quack(); 
}
调用
f(42)
失败,但是只要
f(donald)
i ack
子类型的实例,那么
f(donald)
就可以工作

另一种方法是-但同样,方法
Quack()
是正式指定的,任何不能提前证明它的东西都会导致编译器失败

def f(x : { def Quack() : Unit }) = x.Quack() 
我们甚至可以写

f :: Quackable a => a -> IO ()
f = quack
在Haskell中,
Quackable
typeclass确保了方法的存在


那么鸭式键入如何改变这一点呢

正如我所说的,duck类型系统并没有指定需求,只是在任何情况下进行尝试

因此,Python的动态类型系统总是使用duck类型:

def f(x):
    x.Quack()
如果
f
获得支持
Quack()
x
,则一切正常,否则将在运行时崩溃

但是duck类型根本不意味着动态类型——事实上,有一种非常流行但完全静态的duck类型方法也没有给出任何要求:

template <typename T>
void f(T x) { x.Quack(); } 
模板
void f(tx){x.Quack();}

该函数不会以任何方式告诉您它需要一些能发出嘎嘎声的
x
,因此它只会在编译时尝试,如果一切正常,就可以了。

我知道我没有给出一般性的答案。在Ruby中,我们不声明变量或方法的类型——一切都只是某种对象。 所以规则是“类不是类型”

在Ruby中,类从来都不是(好的,几乎从来都不是)类型。相反,对象的类型更多地由该对象的功能来定义。在Ruby中,我们称之为duck类型。如果一个对象走路像鸭子,说话像鸭子,那么解释器很乐意把它当作鸭子对待

例如,您可能正在编写一个例程,将歌曲信息添加到字符串中。如果您来自C#或Java背景,您可能会想写以下内容:

def append_song(result, song)
    # test we're given the right parameters 
    unless result.kind_of?(String)
        fail TypeError.new("String expected") end
    unless song.kind_of?(Song)
        fail TypeError.new("Song expected")
end

result << song.title << " (" << song.artist << ")" end
result = ""

append_song(result, song) # => "I Got Rhythm (Gene Kelly)"
def append_宋(结果,宋)
#测试我们得到了正确的参数
除非结果。种类?(字符串)
失败类型错误。新建(“应为字符串”)结束
除非是歌曲。是什么?(歌曲)
失败类型错误。新建(“预期歌曲”)
结束

结果假设您正在设计一个简单的函数,该函数获取类型为
Bird
的对象,并调用其
walk()
方法。您可以想到两种方法:

  • 这是我的函数,我必须确保它只接受
    Bird
    类型,否则代码将无法编译。如果有人想使用我的函数,他们必须知道我只接受
    Bird
    s
  • 我的函数获取任何
    对象
    ,我只调用对象的
    walk()
    方法。因此,如果
    对象
    可以
    walk()
    那么它是正确的。如果不能,我的功能就会失败。因此,这里不重要的是对象是
    或其他任何东西,重要的是它可以
    行走()
    (这是duck类型)
  • 必须考虑的是,duck类型在某些情况下可能有用。例如,Python经常使用duck类型


    有用的读物
    • Java、Python和JavaScript都有很好的duck类型示例 等等
    • 这里还有一个很好的答案,描述了动态 打字及其缺点:
    简单解释(无代码) duck类型中重要的是对象实际可以做什么,而不是对象是什么

    什么是鸭子打字? “如果它走路像鸭子,嘎嘎叫像鸭子,那它就是鸭子。”-是的!但这意味着什么呢??!考虑这个例子:

    Duck键入功能的示例: 想象一下我有一根魔杖。它有特殊的权力。如果我挥动魔杖,对一辆汽车说“开车!”
    ,那么,它就会开车

    它对其他东西有用吗?不确定:所以我在卡车上试了一下。哇-它也能开车!然后我在飞机上、火车上和树林里(他们是一种人们用来“驾驶”高尔夫球的高尔夫球杆)试了试<他们都开车

    但它能在茶杯上工作吗?错误:KAAAA-booooom!结果不太好。=>茶杯不能开车!!嗯

    这基本上就是duck类型的概念。这是一个先试后买的系统。如果它起作用,一切都很好。但如果失败了,就像手榴弹还在你手里一样,它会在你脸上爆炸

    换句话说,我们对o
    def append_song(result, song)
        result << song.title << " (" << song.artist << ")"
    end
    
    result = ""
    append_song(result, song) # => "I Got Rhythm (Gene Kelly)"
    
    interface StorageInterface
    {
       public function write(string $key, array $value): bool;
       public function read(string $key): array;
    }
    
    
    class File implements StorageInterface
    {
        public function read(string $key): array {
            //reading from a file
        }
    
        public function write(string $key, array $value): bool {
             //writing in a file implementation
        }
    }
    
    
    class Session implements StorageInterface
    {
        public function read(string $key): array {
            //reading from a session
        }
    
        public function write(string $key, array $value): bool {
             //writing in a session implementation
        }
    }
    
    
    class Storage implements StorageInterface
    {
        private $_storage = null;
    
        function __construct(StorageInterface $storage) {
            $this->_storage = $storage;
        }
    
        public function read(string $key): array {
            return $this->_storage->read($key);
        }
    
        public function write(string $key, array $value): bool {
            return ($this->_storage->write($key, $value)) ? true : false;
        }
    }
    
    $file = new Storage(new File());
    $file->write('filename', ['information'] );
    echo $file->read('filename');
    
    $session = new Storage(new Session());
    $session->write('filename', ['information'] );
    echo $session->read('filename');
    
    function __construct(StorageInterface $storage) ...
    
    def traverse(t):
        try:
            t.label()
        except AttributeError:
            print(t, end=" ")
        else:
            # Now we know that t.node is defined
            print('(', t.label(), end=" ")
            for child in t:
                traverse(child)
            print(')', end=" ")
    
    class MyAwesomeClass:
        def __init__(self, str):
            self.str = str
        
        def __len__(self):
            return len(self.str)
    
    class MyNotSoAwesomeClass:
        def __init__(self, str):
            self.str = str
    
    a = MyAwesomeClass("hey")
    print(len(a))  # Prints 3
    
    b = MyNotSoAwesomeClass("hey")
    print(len(b))  # Raises a type error, object of type "MyNotSoAwesomeClass" has no len()