Gõ vịt là gì?


428

Tôi đã bắt gặp thuật ngữ gõ vịt trong khi đọc các chủ đề ngẫu nhiên trên phần mềm trực tuyến và không hoàn toàn hiểu nó.

Gõ vịt gõ là gì?


1
@Mitch tôi đã thử và có một cái gì đó như một hình thức thừa kế của nó. Nhưng không thể làm theo nhiều. Xin lỗi nếu tôi hỏi câu hỏi sai.
sushil bharwani

3
@sushil bharwani: không, không tức giận. Nhưng mọi người mong đợi rằng là cổng đầu tiên của cuộc gọi (tức là điều đầu tiên bạn làm) là thử tìm kiếm trước khi đăng ở đây.
Mitch Wheat

104
Với các đối số ở trên, dường như không thực sự cần stackoverflow vì tôi chắc chắn rằng hầu hết mọi câu hỏi mà người ta có thể nghĩ đến đều được trả lời ở đâu đó trên internet, và nếu không, câu trả lời có thể dễ dàng nhận được hơn và không bị chỉ trích bằng cách gửi email người bạn hiểu biết. Tôi nghĩ rằng nhiều bạn đã bỏ lỡ điểm stackoverflow.
vần điệu

41
Tôi chắc chắn rằng tôi đã đọc ở đâu đó rằng SO được dự định là "kho lưu trữ các câu hỏi chính tắc" và tôi khá chắc chắn rằng bạn không thể có được nhiều kinh điển hơn câu hỏi này.
heltonbiker

Câu trả lời:


302

Đây là một thuật ngữ được sử dụng trong các ngôn ngữ động không có kiểu gõ mạnh .

Ý tưởng là bạn không cần một loại để gọi một phương thức hiện có trên một đối tượng - nếu một phương thức được định nghĩa trên nó, bạn có thể gọi nó.

Cái tên này xuất phát từ câu "Nếu nó trông giống như một con vịt và quạ giống như một con vịt, thì đó là một con vịt".

Wikipedia có nhiều thông tin hơn.


24
Hãy cảnh giác khi sử dụng Typing mạnh. Nó không được xác định rõ. Không phải là gõ vịt. Google Go hoặc Ocaml là các ngôn ngữ được nhập tĩnh với cấu trúc phụ cấu trúc. Là những con vịt gõ ngôn ngữ?
TÔI CHO CÂU TRẢ LỜI CRAP

7
một cụm từ tốt hơn để gõ vịt là: "Nếu nói đó là một con vịt .. thì điều đó đủ tốt cho tôi." xem pyvideo.org/video/1669/keynote-3 28:30 hoặc youtube.com/watch?v=NfngrdLv9ZQ#t=1716
tovmeod

7
Gõ vịt không nhất thiết chỉ được sử dụng trong các ngôn ngữ động. Objective-C không phải là ngôn ngữ động và nó sử dụng kiểu gõ vịt.
Eyuelt

12
Cả Python và Ruby đều là những ngôn ngữ được gõ mạnh và cả hai đều có Duck Typing. Nhập chuỗi không ngụ ý trong việc không có Duck Typing.
alanjds

8
Tôi đang hạ thấp điều này. Vịt vịt không liên quan gì đến sức mạnh của loại, chỉ là khả năng có thể sử dụng bất kỳ đối tượng nào có phương thức, khi nó có thực hiện giao diện hay không.
e-satis

209

Gõ vịt có nghĩa là một hoạt động không chính thức xác định các yêu cầu mà toán hạng của nó phải đáp ứng, mà chỉ thử nó với những gì được đưa ra.

Không giống như những gì người khác đã nói, điều này không nhất thiết liên quan đến ngôn ngữ động hoặc các vấn đề kế thừa.

Nhiệm vụ ví dụ: Gọi một số phương thức Quacktrên một đối tượng.

Không sử dụng kiểu gõ vịt, một hàm fthực hiện nhiệm vụ này phải xác định trước rằng đối số của nó phải hỗ trợ một số phương thức Quack. Một cách phổ biến là sử dụng giao diện

interface IQuack { 
    void Quack();
}

void f(IQuack x) { 
    x.Quack(); 
}

Gọi f(42)không thành công, nhưng f(donald)hoạt động miễn donaldlà một ví dụ của một IQuack-subtype.

Một cách tiếp cận khác là gõ cấu trúc - nhưng một lần nữa, phương Quack()thức được chỉ định chính thức bất kỳ thứ gì không thể chứng minh quacktrước nó sẽ gây ra lỗi trình biên dịch.

def f(x : { def Quack() : Unit }) = x.Quack() 

Chúng tôi thậm chí có thể viết

f :: Quackable a => a -> IO ()
f = quack

trong Haskell, nơi kiểu chữ Quackableđảm bảo sự tồn tại của phương thức của chúng ta.


Vậy làm thế nào để gõ vịt thay đổi điều này?

Vâng, như tôi đã nói, một hệ thống gõ vịt không chỉ định các yêu cầu mà chỉ thử nếu có bất cứ điều gì hoạt động .

Do đó, một hệ thống kiểu động như Python luôn sử dụng kiểu gõ vịt:

def f(x):
    x.Quack()

Nếu fnhận được xhỗ trợ a Quack(), mọi thứ đều ổn, nếu không, nó sẽ bị sập khi chạy.

Nhưng gõ vịt không bao hàm ý định gõ động - thực tế, có một cách tiếp cận gõ vịt rất phổ biến nhưng hoàn toàn tĩnh mà không đưa ra bất kỳ yêu cầu nào:

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

Hàm không nói theo bất kỳ cách nào mà nó muốn một số xthứ có thể Quack, vì vậy thay vào đó nó chỉ cố gắng vào thời gian biên dịch và nếu mọi thứ hoạt động, nó vẫn ổn.


5
ý bạn không phải là: void f (IQuak x) {x.Quak (); } (thay vì K.Quack) vì tham số của hàm f là IQuack x không phải Iquack k, một lỗi rất nhỏ nhưng tôi cảm thấy cần phải sửa :)
dominicbri7

Theo Wikipedia, ví dụ cuối cùng của bạn là "gõ cấu trúc", không phải "gõ vịt".
Brilliand

Chà, có vẻ như có một câu hỏi riêng cho cuộc thảo luận đó: stackoverflow.com/questions/1948069/iêu
Brilliand

1
Vì vậy, nếu tôi hiểu những gì bạn nói, sự khác biệt giữa ngôn ngữ hỗ trợ gõ vịt và ngôn ngữ không chỉ là gõ vịt, bạn không phải chỉ định loại đối tượng mà hàm chấp nhận? def f(x)thay vì def f(IQuack x).
PProteus

124

Giải thích đơn giản (không có mã)

Thảo luận về ngữ nghĩa của câu hỏi khá sắc thái (và rất hàn lâm), nhưng đây là ý tưởng chung:

Gõ vịt

(Voi Nếu nó đi như vịt và quạ như vịt thì đó là vịt.) - CÓ! Nhưng điều đó có nghĩa gì??! Điều này được minh họa tốt nhất bằng ví dụ:

Ví dụ về chức năng Gõ vịt:

Hãy tưởng tượng tôi có một cây đũa thần. Nó có sức mạnh đặc biệt. Nếu tôi vẫy đũa phép và nói "Lái xe!" đến một chiếc xe, sau đó, nó lái xe!

Nó hoạt động trên những thứ khác? Không chắc chắn: vì vậy tôi thử nó trên một chiếc xe tải. Wow - nó cũng lái xe! Sau đó tôi thử nó trên máy bay, xe lửa và 1 Rừng (chúng là một loại câu lạc bộ golf mà mọi người sử dụng để 'lái' một quả bóng golf). Tất cả đều lái xe!

Nhưng nó sẽ hoạt động để nói, một tách trà? Lỗi: KAAAA-BOOOOOOM! điều đó không tốt lắm ====> Teacup không thể lái xe !! tât nhiên!?

Đây là cơ bản của khái niệm gõ vịt. Đây là một hệ thống thử trước khi mua . Nếu nó hoạt động, tất cả đều tốt. Nhưng nếu nó thất bại, như một quả lựu đạn vẫn còn trong tay bạn, nó sẽ nổ tung trên mặt bạn.

Nói cách khác, chúng ta quan tâm đến những gì đối tượng có thể làm , hơn là với những gì đối tượng là .

Ví dụ: ngôn ngữ gõ tĩnh

Nếu chúng ta quan tâm đến vật thể thực sự là gì , thì trò ảo thuật của chúng ta sẽ chỉ hoạt động trên các loại được đặt sẵn, được ủy quyền - trong trường hợp này là ô tô, nhưng sẽ thất bại với các vật thể khác có thể lái : xe tải, xe máy, tuk-tuks, v.v. Nó sẽ không hoạt động trên xe tải vì cây đũa thần của chúng tôi hy vọng nó chỉ hoạt động trên ô tô .

Nói cách khác, trong trường hợp này, Magic Wand vẻ rất chặt chẽ vào những gì đối tượng (là nó một chiếc xe hơi?) Chứ không phải là những gì mà đối tượng có thể làm (ví dụ như xe hơi, xe tải vv có thể lái xe).

Cách duy nhất bạn có thể khiến một chiếc xe tải lái xe là nếu bạn bằng cách nào đó có thể có được cây đũa thần để mong đợi cả xe tải xe hơi (có lẽ bằng cách "thực hiện một giao diện chung"). Nếu bạn không biết điều đó có nghĩa là gì thì hãy bỏ qua nó ngay bây giờ.

Tóm tắt: Lấy chìa khóa

Có gì quan trọng trong việc gõ vịt là những gì các đối tượng thực sự có thể làm, chứ không phải là những gì mà đối tượng .


Tôi thấy thú vị tiền đề về việc bạn quan tâm nhiều hơn đến hành vi, đó là định nghĩa. Không còn nghi ngờ gì nữa, BDD rất thành công trong các ngôn ngữ như ruby.
Pablo Olmos de Aguilera C.

27

Hãy xem xét bạn đang thiết kế một hàm đơn giản, lấy một đối tượng kiểu Birdvà gọi walk()phương thức của nó . Có hai cách tiếp cận mà bạn có thể nghĩ ra:

  1. Đây là chức năng của tôi và tôi phải chắc chắn rằng nó chỉ chấp nhận Bird, hoặc mã của họ sẽ không biên dịch. Nếu bất cứ ai muốn sử dụng chức năng của tôi, anh ta phải nhận thức được rằng tôi chỉ chấp nhận Birds
  2. Hàm của tôi nhận được bất kỳ objectsvà tôi chỉ gọi walk()phương thức của đối tượng . Vì vậy, nếu nó objectcó thể walk()đúng, nếu nó không thể thì chức năng của tôi sẽ thất bại. Vì vậy, ở đây không quan trọng đối tượng là một Birdhoặc bất cứ điều gì khác, điều quan trọng là nó có thể walk() (Đây là gõ vịt )

Phải xem xét rằng việc gõ vịt có thể hữu ích trong một số trường hợp, ví dụ Python sử dụng gõ vịt rất nhiều.


Đọc hữu ích


1
Giải thích tốt, bất kỳ lợi thế là gì?
sushil bharwani

2
Câu trả lời này là đơn giản, rõ ràng và có lẽ là tốt nhất cho người mới bắt đầu. Đọc câu trả lời này cùng với câu trả lời ở trên (hoặc nếu nó di chuyển, câu trả lời nói về xe hơi và tách trà)
DORRITO

18

Wikipedia có một lời giải thích khá chi tiết:

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

gõ vịt là một kiểu gõ động trong đó tập hợp các phương thức và thuộc tính hiện tại của một đối tượng xác định ngữ nghĩa hợp lệ, thay vì kế thừa từ một lớp cụ thể hoặc thực hiện một giao diện cụ thể.

Lưu ý quan trọng có thể là với việc gõ vịt, một nhà phát triển quan tâm nhiều hơn đến các bộ phận của đối tượng được tiêu thụ hơn là loại cơ bản thực sự là gì.


13

Tôi thấy rất nhiều câu trả lời lặp lại thành ngữ cũ:

Nếu nó trông giống như một con vịt và quạ giống như một con vịt, thì đó là một con vịt

và sau đó đi sâu vào một lời giải thích về những gì bạn có thể làm với việc gõ vịt, hoặc một ví dụ dường như làm xáo trộn khái niệm này hơn nữa.

Tôi không tìm thấy nhiều sự giúp đỡ.

Đây là nỗ lực tốt nhất trong câu trả lời bằng tiếng Anh đơn giản về cách gõ vịt mà tôi đã tìm thấy:

Duck Typing có nghĩa là một đối tượng được xác định bởi những gì nó có thể làm, không phải bởi những gì nó là.

Điều này có nghĩa là chúng ta ít quan tâm đến lớp / loại đối tượng và quan tâm nhiều hơn đến phương thức nào có thể được gọi trên nó và hoạt động nào có thể được thực hiện trên nó. Chúng tôi không quan tâm đến loại của nó, chúng tôi quan tâm đến những gì nó có thể làm .


3

Gõ vịt:

Nếu nó nói và đi như một con vịt, thì đó là một con vịt

Đây thường được gọi là vụ bắt cóc ( abductive lý hay còn gọi là retroduction , một định nghĩa rõ ràng hơn tôi nghĩ):

  • từ C (kết luận, những gì chúng ta thấy ) và R (quy tắc, những gì chúng ta biết ), chúng ta chấp nhận / quyết định / giả định P (Tiền đề, tài sản ) nói cách khác là một thực tế nhất định

    ... cơ sở của chẩn đoán y khoa

    với vịt: C = đi bộ, nói chuyện , R = như một con vịt , P = đó là một con vịt

Quay lại lập trình:

  • đối tượng o có phương thức / thuộc tính mp1 và giao diện / loại T yêu cầu / định nghĩa mp1

  • đối tượng o có phương thức / thuộc tính mp2 và giao diện / loại T yêu cầu / định nghĩa mp2

  • ...

Vì vậy, không chỉ đơn giản là chấp nhận mp1 ... trên bất kỳ đối tượng nào, miễn là nó đáp ứng một số định nghĩa của mp1 ..., trình biên dịch / thời gian chạy cũng sẽ ổn với xác nhận o thuộc loại T

Và tốt, đó là trường hợp với các ví dụ ở trên? Có phải gõ vịt về cơ bản là không gõ gì cả? Hay chúng ta nên gọi nó là gõ ngầm?


3

Nhìn vào ngôn ngữ có thể giúp đỡ; nó thường giúp tôi (tôi không phải là người nói tiếng Anh bản ngữ).

Trong duck typing:

1) từ typingnày không có nghĩa là gõ trên bàn phím (như hình ảnh liên tục trong tâm trí của tôi), nó có nghĩa là xác định " loại vật đó là gì? "

2) từ này duckdiễn tả cách xác định đó được thực hiện; đó là một quyết định 'lỏng lẻo', như trong: " nếu nó đi như một con vịt ... thì đó là một con vịt ". Đó là 'lỏng lẻo' bởi vì thứ đó có thể là một con vịt hoặc có thể không, nhưng liệu nó có thực sự là một con vịt không; Điều quan trọng là tôi có thể làm gì với nó Những gì tôi có thể làm với vịt và mong đợi những hành vi được thể hiện bởi vịt. Tôi có thể cho nó ăn vụn bánh mì và mọi thứ có thể tiến về phía tôi hoặc buộc tội tôi hoặc rút lui ... nhưng nó sẽ không nuốt chửng tôi như một con sâu xám.


2

Tôi biết tôi không đưa ra câu trả lời khái quát. Trong Ruby, chúng tôi không khai báo các loại biến hoặc phương thức. Mọi thứ chỉ là một loại đối tượng. Vì vậy, quy tắc là "Các loại không phải là loại"

Trong Ruby, lớp không bao giờ (OK, gần như không bao giờ) kiểu. Thay vào đó, loại đối tượng được xác định nhiều hơn bởi những gì đối tượng đó có thể làm. Trong Ruby, chúng tôi gọi đây là gõ vịt. Nếu một đối tượng đi như một con vịt và nói chuyện như một con vịt, thì người phiên dịch rất vui khi coi nó như một con vịt.

Ví dụ, bạn có thể đang viết một thói quen để thêm thông tin bài hát vào chuỗi. Nếu bạn đến từ nền tảng C # hoặc Java, bạn có thể muốn viết điều này:

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

Ôm vịt gõ Ruby, và bạn sẽ viết một cái gì đó đơn giản hơn nhiều:

def append_song(result, song)
    result << song.title << " (" << song.artist << ")"
end

result = ""
append_song(result, song) # => "I Got Rhythm (Gene Kelly)"

Bạn không cần phải kiểm tra loại đối số. Nếu họ hỗ trợ << (trong trường hợp kết quả) hoặc tiêu đề và nghệ sĩ (trong trường hợp bài hát), mọi thứ sẽ chỉ hoạt động. Nếu họ không, phương thức của bạn sẽ đưa ra một ngoại lệ (giống như cách bạn đã làm nếu bạn đã kiểm tra các loại). Nhưng không có kiểm tra, phương pháp của bạn đột nhiên linh hoạt hơn rất nhiều. Bạn có thể truyền cho nó một mảng, một chuỗi, một tệp hoặc bất kỳ đối tượng nào khác nối thêm bằng cách sử dụng << và nó sẽ chỉ hoạt động.


2

Gõ vịt không phải là Gợi ý!

Về cơ bản để sử dụng "gõ vịt", bạn sẽ không nhắm mục tiêu một loại cụ thể mà thay vào đó là một phạm vi rộng hơn của các kiểu con (không nói về thừa kế, khi tôi nói là các kiểu con tôi có nghĩa là "những thứ" phù hợp trong cùng một cấu hình) bằng cách sử dụng một giao diện chung .

Bạn có thể tưởng tượng một hệ thống lưu trữ thông tin. Để viết / đọc thông tin, bạn cần một số loại lưu trữ và thông tin.

Các loại lưu trữ có thể là: tệp, cơ sở dữ liệu, phiên, v.v.

Giao diện sẽ cho bạn biết các tùy chọn có sẵn (phương thức) bất kể loại lưu trữ, có nghĩa là tại thời điểm này không có gì được thực hiện! Nói cách khác, Giao diện không biết gì về cách lưu trữ thông tin.

Mọi hệ thống lưu trữ phải biết sự tồn tại của giao diện bằng cách thực hiện các phương thức rất giống nhau.

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;
    }
}

Vì vậy, bây giờ, mỗi khi bạn cần viết / đọc thông tin:

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

Trong ví dụ này, bạn kết thúc bằng cách sử dụng Duck Typing trong Storage constructor:

function __construct(StorageInterface $storage) ...

Hy vọng nó sẽ giúp;)


2

Tree Traversal với kỹ thuật gõ vịt

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

0

Tôi nghĩ thật khó hiểu khi trộn lẫn giữa gõ động, gõ tĩnh và gõ vịt. Gõ vịt là một khái niệm độc lập và thậm chí ngôn ngữ gõ tĩnh như Go, có thể có một hệ thống kiểm tra kiểu thực hiện gõ vịt. Nếu một hệ thống kiểu sẽ kiểm tra các phương thức của một đối tượng (được khai báo) nhưng không phải là kiểu, nó có thể được gọi là ngôn ngữ gõ vịt.


-1

Tôi cố gắng hiểu câu nổi tiếng theo cách của tôi: "Liều Python không quan tâm đến một đối tượng có phải là một con vịt thực sự hay không. Tất cả những gì nó quan tâm là liệu đối tượng đó, đầu tiên là 'quạc', thứ hai 'như một con vịt'."

Có một trang web tốt. http://www.voidspace.org.uk/python/articles/duck_typing.shtml#id14

Tác giả đã chỉ ra rằng gõ vịt cho phép bạn tạo các lớp riêng có cấu trúc dữ liệu nội bộ của riêng mình - nhưng được truy cập bằng cú pháp Python bình thường.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.