鸭子类型在软件开发中意味着什么?


当前回答

用鸭子打字技术的树遍历

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=" ")

其他回答

在duck类型中,对象的适用性(例如,在函数中使用)取决于是否实现了某些方法和/或属性,而不是基于该对象的类型。

例如,在Python中,len函数可用于任何实现__len__方法的对象。它并不关心该对象是否属于特定类型,例如字符串、列表、字典或MyAwesomeClass,只要这些对象实现了__len__方法,len将与它们一起工作。

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()

换句话说,MyAwesomeClass看起来像鸭子,也像鸭子一样嘎嘎叫,因此是一只鸭子,而MyNotSoAwesomeClass看起来不像鸭子,也不嘎嘎叫,因此不是一只鸭子!

它是一个用于没有强类型的动态语言中的术语。

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

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

维基百科有更多的信息。

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

http://en.wikipedia.org/wiki/Duck_typing

鸭子打字是一种动态的风格 输入一个对象的电流 方法和属性集 而是确定有效的语义 比它从一个特定的继承 类的实现 接口。

重要的是,使用duck类型时,开发人员可能更关心被使用的对象部分,而不是实际的底层类型是什么。

鸭子打字不是类型提示!

基本上,为了使用“duck typing”,你不会针对特定的类型,而是通过使用公共接口来针对更广泛的子类型(不是谈论继承,当我指的子类型时,我指的是适合相同配置文件的“事物”)。

你可以想象一个存储信息的系统。为了读写信息,你需要某种存储空间和信息。

存储类型可以是:文件、数据库、会话等。

无论存储类型是什么,该接口都会让您知道可用的选项(方法),这意味着在这一点上什么都没有实现!换句话说,接口不知道如何存储信息。

每个存储系统都必须通过实现接口的相同方法来知道接口的存在。

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');

在这个例子中,你最终在存储构造函数中使用Duck Typing:

function __construct(StorageInterface $storage) ...

希望能有所帮助;)

Duck typing:

如果它像鸭子一样说话和走路,那么它就是一只鸭子

这通常被称为诱拐(诱拐推理或也称为归纳,我认为一个更清晰的定义):

从C(结论,我们所看到的)和R(规则,我们所知道的),我们接受/决定/假设P(前提,属性),换句话说,一个给定的事实 ... 医学诊断的基础 和鸭子:C =走路,说话,R =像鸭子,P =它是一只鸭子

回到编程:

对象o有方法/属性mp1和接口/类型T 要求/定义mp1 对象o有方法/属性mp2,接口/类型T要求/定义mp2 ...

因此,不仅仅是简单地接受mp1…在任何对象上,只要它满足mp1的某些定义…,编译器/运行时也应该接受断言o是类型T

上面的例子是这样的吗?Duck输入实质上就是没有输入吗?或者我们应该称之为隐式类型?