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ì?
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ì?
Câu trả lời:
Đâ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.
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 Quack
trên một đối tượng.
Không sử dụng kiểu gõ vịt, một hàm f
thự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 donald
là 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 quack
trướ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â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 f
nhận được x
hỗ 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ố x
thứ 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.
def f(x)
thay vì def f(IQuack x)
.
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à (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 và 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 là .
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 Bird
và 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:
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 Bird
sobjects
và tôi chỉ gọi walk()
phương thức của đối tượng . Vì vậy, nếu nó object
có 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 Bird
hoặ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.
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ì.
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 .
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?
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ừ typing
nà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 duck
diễ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.
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.
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;)
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.
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.