轉自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&id=4662991&uid=608135
作為萬年Perl 黨表示最近開始學Python 了,下面會記錄一下學習中Python 和Perl 的對比,如果你也是一個Perl 用戶,看過了也會對Python 有一個大致的印象吧。 事實上,寫着寫着我發現如果你是一名Python 用戶,看完后也會對Perl 有一個大致的了解 _(:з)∠)_ 基本數據類型 1. Perl 中的標量 a. Perl 中的標量在Python 中對應為數字類型和字符串類型 Perl 的標量是字符串還是數值會根據上下文自動判斷,但是Python 不會這樣做。 下面的代碼是你在Perl 中習以為常的操作 [plain] view plaincopy在CODE上查看代碼片派生到我的代碼片 my ($string, $number) = ("1", ); $number = $string + "1"; # $string 和"1" 都被解釋為數值 say $number; $string = $number.1; # $number 和1 都被解釋為字符串 say $string; 但是Python 中你必須顯式的指定數據類型 [python] view plaincopy在CODE上查看代碼片派生到我的代碼片 string = "1" number = int (string) + 1 # 必須顯式轉換為數值類型 print (number) string = str (number) + "1" # 同樣必須顯式轉換為字符串 print (string) b. Perl 的標量不支持下標運算,但是Python 的字符串可以 Python 中你可以很方便的用下標索引字符串(而且和Perl 中的列表一樣也支持用負數做反向索引) [python] view plaincopy在CODE上查看代碼片派生到我的代碼片 s = "A string" print (s[2:]) 但是Perl 的標量就無法這樣操作,相同的操作必須用列表切片完成 [plain] view plaincopy在CODE上查看代碼片派生到我的代碼片 $_ = "A string"; say ((split //)[2..split //]); c. Perl 中的heredoc 式標量賦值在Python 中可以使用三引號運算符完成 [plain] view plaincopy在CODE上查看代碼片派生到我的代碼片 @_ = < Everthing is in the array END say "@_"; 下面是等價的Python 代碼,注意第一行是緊跟三引號運算符之后的 [python] view plaincopy在CODE上查看代碼片派生到我的代碼片 string = '''''Everthing is in the array ''' print (string) 2. Perl 中的列表 a. Perl 中的列表Python 中對應為列表和元組 下面是Python 中倒序排序一個列表的操作 [python] view plaincopy在CODE上查看代碼片派生到我的代碼片 my_list = ["Hello", "Python"] my_list.append ("World") my_list += ["!"] print (my_list) my_list.sort (); my_list.reverse (); for string in my_list: print (string) 對應的Perl 同性質操作的版本 [plain] view plaincopy在CODE上查看代碼片派生到我的代碼片 @_ = qw/Hello Perl/; push @_, 'World'; @_ = (@_, '!'); print "@_\n"; @_ = reverse sort @_; say for @_; b. 常用的splice 操作在Python 中可以用過index () 方法和del 操作完成 下面是Perl 中常用的splice 操作 [plain] view plaincopy在CODE上查看代碼片派生到我的代碼片 @_ = qw/Hello Perl !/; splice @_, 2, 0, "World"; # 插入元素 say "@_"; my @removed = splice @_, 1, 2; # 提取一段元素並在原列表刪除 print "@_\n@removed\n"; Python 中對應的操作 [python] view plaincopy在CODE上查看代碼片派生到我的代碼片 my_list = ["Hello", "Python", "!"] my_list.insert (2, "World") # 插入元素 print (my_list) removed = my_list[1:2] # 提取一段元素 del my_list[1:2] # 並在原列表刪除 print (my_list) print (removed) 注意:Python 中元組和列表的區別 元組創建后是只讀的,但是列表隨時都可以被修改。 3. Perl 中的哈希 a. Perl 中的哈希Python 中對應為字典和集合 一個Perl 哈希按照鍵值升序遍歷的操作如下 [plain] view plaincopy在CODE上查看代碼片派生到我的代碼片 my %hash = ( "first" => 1, "second" => 2, "third" => 3 ); say "$_ => $hash{$_}" for sort keys %hash; Python 中代碼如下(注意定義變為花括號) [python] view plaincopy在CODE上查看代碼片派生到我的代碼片 my_hash = { "first" : 1, "second" : 2, "third" : 3 } items = sorted (my_hash.items (), key=lambda e:e[1], reverse=False) for item in items: print (item[0], ':', item[1]) 注意:Python 中集合與字典的不同之處 (a) 僅包含鍵,但是沒有值 (b) set 可變,但是frozenset 不可變(如其名) 4. Python 強類型的判斷和賦值 一個非常大的不同之處:Perl 中賦值號默認的行為是拷貝,但是Python 中賦值號會創建一個對象的引用。 另外,因為和弱類型的Perl 不同,Python 是一門強類型語言,所以也有判斷對象類型的必要。 下面代碼演示了這些區別,首先是一段Python 代碼 [python] view plaincopy在CODE上查看代碼片派生到我的代碼片 lst = [1, 2, 3] lst2 = lst # lst2 是lst 的一個引用 print (lst) lst2[1] = 2.5 # 所以修改lst2 也會引起lst 的變化 print (lst) lst3 = copy.deepcopy (lst) # lst3 是lst 的一個深拷貝 lst3[1] = 2 print (lst) # 修改lst3 不會引起lst 的變化 # 因為Python 是強類型語言,所以需要類型判斷操作 if lst == lst2: # 如果s 和s2 值相同 print ("lst equal to lst2") if lst is lst2: # 如果s 和s2 是指向同一個對象的引用 print ("lst and lst2 is the same") if type (lst) is type (lst3): # 如果s 和s3 類型相同 print ("lst and lst3 has same type") 下面是與之完全等價的Perl 代碼,對比一下你就會很快了解其中的差別 [plain] view plaincopy在CODE上查看代碼片派生到我的代碼片 my @lst = (1..3); my $lst2 = \@lst; # lst2 是lst 的一個引用 say "@$lst2"; $lst2->[1] = 2.5; # 所以修改lst2 也會引起lst 的變化 say "@$lst2"; my @lst3 = @lst; # lst3 是lst 的一個深拷貝 $lst3[1] = 2; say "@lst"; # 修改lst3 不會引起lst 的變化 =pod 因為Perl 是弱類型語言,所以不需要類型判斷操作 但是Perl 中仍然可以做這樣的操作——雖然沒什么意義 =cut say "lst equal to lst2" if @lst == @$lst2; # 如果s 和s2 值相同 say "lst and lst2 is the same" if \@lst == $lst2; # 如果s 和s2 是指向同一個對象的引用 say "lst and lst3 has same type" if ref \@lst eq ref \@lst3; # 如果s 和s3 類型相同 5. Perl 中的“上下文” 上下文的判斷幾乎已經成了一名Perl 用戶不可或缺的第六感,一個顯而易見的例子是當你定義一個列表 [plain] view plaincopy在CODE上查看代碼片派生到我的代碼片 my @lst = (1..3); 當你循環遍歷的時候,你深知把for 循環 [plain] view plaincopy在CODE上查看代碼片派生到我的代碼片 say for @lst; # 列表上下文 替換為While 循環 [plain] view plaincopy在CODE上查看代碼片派生到我的代碼片 say while @lst; # 標量上下文 究竟會得到什么災難性的后果。 但是在Python 中你不必擔心這個:Python 對於數據類型的解釋很明確,不會因為上下文環境不同而改變,當你定義一個列表 [python] view plaincopy在CODE上查看代碼片派生到我的代碼片 lst = [1, 2, 3] 之后,你永遠不必擔心在什么特殊的上下文中lst 會被Python 解釋為數值3。 面向對象編程 1. 幾點明顯的異同 Perl 和Python 的面向對象給人的感覺是后者明顯比前者更加規范,下面是自己感受最明顯的幾點異同。 a. 真的是面向對象么? 1). Perl 中的“面向對象”更像是“面向過程”的文字游戲:類通過包實現、方法通過包中定義的函數實現、類的繼承和方法的重載通過@ISA 列表查找循序實現、私有方法通過指向匿名函數的my 引用實現(因為my變量是本文件可見的)、運算符重載通過指向函數的引用實現、對象中成員變量的訪問通過bless 到類名的引用實現……這樣如果說有好處,那就是編程可以非常靈活——你可以用一個父類的多個子類共同繼承出一個類(例如從哺乳動物中人類和貓類繼承出一種新生物),Perl 完全對這種行為不在意,只要你確信這種近親結婚的方式真的是你需要達到的目的。當然壞處顯而易見——你可以輕而易舉把代碼寫的糟糕透頂。所以Perl 的“面向對象”給人的感覺只是“面向過程”的另一種玩法——面向過程的本質沒變,但是面向對象的效果達到了。 2). Python 中的“面向對象”比Perl 要嚴謹和規范許多,非常類似於Java,如果你熟悉Java 或者C++,那么你會很好理解Python 的面向對象編程。 b. 包和類 1). Perl 中兩者完全等價,一個包就是一個類(pm 是Perl 模塊的意思,但是它又被叫做包,而包就是類的意思 ← ←)。 2). Python 中一個包可以包含多個模塊,一個模塊可以包含多個類。 c. 靜態方法 Perl 和Python 中靜態方法都是第一個參數不是類的引用的方法,但是稍有不同: 1). Perl 中靜態方法第一個參數是類名,可以通過bless 新的引用到類名來操作對象類型(例如你在構造方法里做的那樣)。 2). Python 中靜態方法完全無法操作對象類型。 d. 私有方法: 1). Perl 中的私有方法通過my 變量只有當前文件可見的性質,用保存匿名函數的my 引用來達到“私有”的目的(“面向對象”的文字游戲)。 2). Python 中吧以“__”開頭的方法都當作私有方法,通過方法名會變成"_類名__方法名" 的形式來避免其他類調用該方法,但是你仍然可以通過手動變換后的方法名直接調用私有方法。 e. 方法的傳參: 1). Perl 中一般將散列的引用bless 到類名,所以傳參可以十分靈活,如果構造函數允許,參數個數和位置根本無關緊要,但是隨之造成的問題就是可能引發混亂。 2). Python 中方法聲明無法把無默認值的參數放在有默認值的參數后面,但是因為實參可以通過給出參數名手動顯式指定,所以次序也可以無關緊要。 f. 運算符重載: 1). Perl 通過use overload 模塊指定方法的引用來達到重載運算符的目的。 2). Python 中通過一組特殊名稱的方法來重載運算符。 g. 父類方法重載: 1). Perl 中通過@ISA 列表的搜索順序來達到重載父類方法的目的(子類的同名方法會被優先搜索到),並且可以顯式SUPER 偽類訪問被覆蓋的基類方法(就如你經常在析構方法中做的一樣)。 2). Python 的重載更加正式,形式非常類似於C++。 h. 繼承: 1). Perl 的繼承只是操作了@ISA 列表,子類中沒有的方法會在@ISA 中尋找方法名,因此這種行為得到的結果和面向對象編程的繼承相同。UNIVERSAL 是所有類的祖先類,提供了isa 方法用來測試繼承關系。 2). Python 的繼承類似於C++,顯式指定了要繼承的父類,object 類是所有類的祖先類,提供issubclass 方法用來測試繼承關系。 2. 一個演示異同的例子 下面的兩個例子都會有相同的輸出,演示了Perl 和Python 中類的構造、析構、公有方法、私有方法、運算符重載、繼承、父類方法重載等。 下面是預期的輸出 [plain] view plaincopy在CODE上查看代碼片派生到我的代碼片 =My name's Lucy, 2 years old. Adoption me please. +I am hungry offen. -My name's Leia, 1 years old. My host is iSpeller. +I hate milk but my host give me offen. -My name's Lucy, 2 years old. My host is iSpeller. +I hate milk but my host give me offen. -------------------------------------------------------- 下面是你熟悉的Perl [plain] view plaincopy在CODE上查看代碼片派生到我的代碼片 #!/usr/bin/perl # ======================== # filename: main.pm # main 類,演示了: # 類的實例化 # ======================= package main; use warnings; use strict; use 5.010; use Dog; use Pet_Dog; push @INC, '.'; # 一條叫Lucy 的汪星人 my $lucy = Dog->new (name => 'Lucy', age => 2); $lucy->say_hello; $lucy->my_secret; # 第一條寵物汪,默認為1 歲的leia my $pet_leia = Pet_Dog->new (host => 'iSpeller'); $pet_leia->say_hello; $pet_leia->my_secret; # 納入第二個寵物汪 # 調用了Pet 類運算符重載函數 my $pet_lucy = $lucy + "iSpeller"; $pet_lucy->say_hello; $pet_lucy->my_secret; 1; [plain] view plaincopy在CODE上查看代碼片派生到我的代碼片 # ======================== # filename: Dog.pm # Pet 類,演示了: # 構造、析構方法 # 公有、私有方法 # 重載 # ======================== package Dog; use strict; use warnings; use 5.010; use overload '+' => \&meet; # 重載加號運算符 # 構造方法 # 是靜態方法,第一個參數為類名 sub new { my $type = shift; my $class = ref $type || $type; # 如有用戶實例變量則覆蓋默認屬性 my $self = { name => 'Leia', age => 1, is_pet => 0, @_ }; bless $self, $class; return $self; } # 析構方法 # 是虛方法,第一個參數為類的引用 sub DESTROY { my $self = shift; # 調用父類析構方法 $self->SUPER::DESTROY if $self->can ('SUPER::DESTROY'); } # 公有方法 sub say_hello { my $self = shift; print '=' if $self->isa ("UNIVERSAL"); # UNIVERSAL 類是所有類的祖先類 printf "My name's %s, %d years old. %s.\n", $self->{name}, $self->{age}, $self->{is_pet} ? "I am a pet dot" : "Adoption me please"; } # 私有方法 my $say_secret = sub { my $self = shift; say '+', $self->{is_pet} ? "I hate milk but my host give me offen." : "I am hungry offen."; }; # 私有方法只能在本文件內由其他方法訪問(my $say_secret) sub my_secret { my $self = shift; $self->$say_secret; } # 重載加號運算符,返回成為寵物后的自身 sub meet { my $self = shift; my @property = %$self; my $new = Pet_Dog->new (@property, host => shift); return $new; } 1; [plain] view plaincopy在CODE上查看代碼片派生到我的代碼片 # ======================== # filename: Pet_Dog.pm # Pet_Dog 類,繼承自Dog 類,演示了: # 繼承、父類方法的重載 # ======================= package Pet_Dog; use strict; use warnings; use 5.010; use base qw/Dog/; # 繼承自Dog 類 sub new { # 調用父類的構造方法 # 因為shift 得到的是子類的類名,所以不需要重新bless my $self = Dog::new (shift, host => "none", @_, is_pet => 1); return $self; } # 重載父類的say_hello (虛)方法 sub say_hello { my $self = shift; print '-' if $self->isa ("Dog"); # 繼承關系測試 printf "My name's %s, %d years old. My host is %s.\n", $self->{name}, $self->{age}, $self->{host}; } 1; -------------------------------------------------------- 下面是完全等價的Python [python] view plaincopy在CODE上查看代碼片派生到我的代碼片 #!/usr/bin/python3 # filename: main.py # ======================== # Dog 類,演示了: # 構造、析構方法 # 公有、私有方法 # 重載 # ======================== class Dog: # 構造方法 # 是私有方法,因為方法名以"__" 開頭 def __init__ (self, name = "Leia", age = 1, is_pet = 0): # 如有用戶實例變量則覆蓋默認屬性 self.name = name self.age = age self.is_pet = is_pet # 析構方法 # 靜態方法,不會操作實例類型 # 類似Perl,第一個參數不是引用,所以你無法通過第一個參數來引用實例變量 @staticmethod def __del__ (): pass # 公有方法 def say_hello (self): if issubclass (Dog, object): print ("=", end='') print ("My name's %s, %d years old. %s." % (self.name, self.age, # Python 中沒有三目運算符,可以用下面的形式替代 "I am a pet dog" if self.is_pet else "Adoption me please")) # 私有方法 def __say_secret (self): print ("+%s." % ("I hate milk but my host give me offen" if self.is_pet else "I am hungry offen")) # 私有方法只能在本類內由其他方法訪問 def my_secret (self): self.__say_secret () # 重載加號運算符為和對方戀愛,返回成為女朋友后的自身 def __add__ (self, other): new = Pet_Dog (self.name, self.age, 1, other) return (new) # ======================== # Pet_Dog 類,繼承自Dog 類,演示了: # 繼承、父類方法的重載 # ======================== class Pet_Dog (Dog): # 調用父類的構造方法 # 之后初始化子類變量 def __init__ (self, name = "Leia", age = 1, is_pet = 1, host = "none"): Dog.__init__ (self, name, age, is_pet) self.host = host # 重載父類的say_hello (虛)方法 def say_hello (self): if issubclass (Pet_Dog, Dog): print ("-", end='') print ("My name's %s, %d years old. My host is %s." % (self.name, self.age, self.host)) ''''' 程序開始,Python 的類型不允許在定義之前使用 然而Python 似乎又不區分聲明和定義 演示了類的實例化 ''' # 一條叫Lucy 的汪星人 lucy = Dog ("Lucy", 2) lucy.say_hello () lucy.my_secret () # 第一條寵物汪,默認為1 歲的leia pet_dog_leia = Pet_Dog (host = "iSpeller"); pet_dog_leia.say_hello () pet_dog_leia.my_secret () # 納入第二寵物汪 # 調用了Pet 類運算符重載函數 pet_dog_lucy = lucy + "iSpeller" pet_dog_lucy.say_hello () pet_dog_lucy.my_secret () 轉載自:http://blog.csdn.net/iSpeller/article/details/23198211
作為萬年Perl 黨表示最近開始學Python 了,下面會記錄一下學習中Python 和Perl 的對比,如果你也是一個Perl 用戶,看過了也會對Python 有一個大致的印象吧。
事實上,寫着寫着我發現如果你是一名Python 用戶,看完后也會對Perl 有一個大致的了解 _(:з)∠)_
基本數據類型
1. Perl 中的標量
a. Perl 中的標量在Python 中對應為數字類型和字符串類型
Perl 的標量是字符串還是數值會根據上下文自動判斷,但是Python 不會這樣做。
下面的代碼是你在Perl 中習以為常的操作
- my ($string, $number) = ("1", );
- $number = $string + "1"; # $string 和"1" 都被解釋為數值
- say $number;
- $string = $number.1; # $number 和1 都被解釋為字符串
- say $string;
但是Python 中你必須顯式的指定數據類型
- string = "1"
- number = int (string) + 1 # 必須顯式轉換為數值類型
- print (number)
- string = str (number) + "1" # 同樣必須顯式轉換為字符串
- print (string)
b. Perl 的標量不支持下標運算,但是Python 的字符串可以
Python 中你可以很方便的用下標索引字符串(而且和Perl 中的列表一樣也支持用負數做反向索引)
- s = "A string"
- print (s[2:])
但是Perl 的標量就無法這樣操作,相同的操作必須用列表切片完成
- $_ = "A string";
- say ((split //)[2..split //]);
c. Perl 中的heredoc 式標量賦值在Python 中可以使用三引號運算符完成
- @_ = <<end; < span="" style="word-wrap: break-word;">
- Everthing is
- in
- the array
- END
- say "@_";
下面是等價的Python 代碼,注意第一行是緊跟三引號運算符之后的
- string = '''''Everthing is
- in
- the array
- '''
- print (string)
2. Perl 中的列表
a. Perl 中的列表Python 中對應為列表和元組
下面是Python 中倒序排序一個列表的操作
- my_list = ["Hello", "Python"]
- my_list.append ("World")
- my_list += ["!"]
- print (my_list)
- my_list.sort ();
- my_list.reverse ();
- for string in my_list:
- print (string)
對應的Perl 同性質操作的版本
- @_ = qw/Hello Perl/;
- push @_, 'World';
- @_ = (@_, '!');
- print "@_\n";
- @_ = reverse sort @_;
- say for @_;
b. 常用的splice 操作在Python 中可以用過index () 方法和del 操作完成
下面是Perl 中常用的splice 操作
- @_ = qw/Hello Perl !/;
- splice @_, 2, 0, "World"; # 插入元素
- say "@_";
- my @removed = splice @_, 1, 2; # 提取一段元素並在原列表刪除
- print "@_\n@removed\n";
Python 中對應的操作
- my_list = ["Hello", "Python", "!"]
- my_list.insert (2, "World") # 插入元素
- print (my_list)
- removed = my_list[1:2] # 提取一段元素
- del my_list[1:2] # 並在原列表刪除
- print (my_list)
- print (removed)
注意:Python 中元組和列表的區別
元組創建后是只讀的,但是列表隨時都可以被修改。
3. Perl 中的哈希
a. Perl 中的哈希Python 中對應為字典和集合
一個Perl 哈希按照鍵值升序遍歷的操作如下
- my %hash = (
- "first" => 1,
- "second" => 2,
- "third" => 3
- );
- say "$_ => $hash{$_}" for sort keys %hash;
Python 中代碼如下(注意定義變為花括號)
- my_hash = {
- "first" : 1,
- "second" : 2,
- "third" : 3
- }
- items = sorted (my_hash.items (), key=lambda e:e[1], reverse=False)
- for item in items:
- print (item[0], ':', item[1])
注意:Python 中集合與字典的不同之處
(a) 僅包含鍵,但是沒有值
(b) set 可變,但是frozenset 不可變(如其名)
4. Python 強類型的判斷和賦值
一個非常大的不同之處:Perl 中賦值號默認的行為是拷貝,但是Python 中賦值號會創建一個對象的引用。
另外,因為和弱類型的Perl 不同,Python 是一門強類型語言,所以也有判斷對象類型的必要。
下面代碼演示了這些區別,首先是一段Python 代碼
- lst = [1, 2, 3]
- lst2 = lst # lst2 是lst 的一個引用
- print (lst)
- lst2[1] = 2.5 # 所以修改lst2 也會引起lst 的變化
- print (lst)
- lst3 = copy.deepcopy (lst) # lst3 是lst 的一個深拷貝
- lst3[1] = 2
- print (lst) # 修改lst3 不會引起lst 的變化
- # 因為Python 是強類型語言,所以需要類型判斷操作
- if lst == lst2: # 如果s 和s2 值相同
- print ("lst equal to lst2")
- if lst is lst2: # 如果s 和s2 是指向同一個對象的引用
- print ("lst and lst2 is the same")
- if type (lst) is type (lst3): # 如果s 和s3 類型相同
- print ("lst and lst3 has same type")
下面是與之完全等價的Perl 代碼,對比一下你就會很快了解其中的差別
- my @lst = (1..3);
- my $lst2 = \@lst; # lst2 是lst 的一個引用
- say "@$lst2";
- $lst2->[1] = 2.5; # 所以修改lst2 也會引起lst 的變化
- say "@$lst2";
- my @lst3 = @lst; # lst3 是lst 的一個深拷貝
- $lst3[1] = 2;
- say "@lst"; # 修改lst3 不會引起lst 的變化
- =pod
- 因為Perl 是弱類型語言,所以不需要類型判斷操作
- 但是Perl 中仍然可以做這樣的操作——雖然沒什么意義
- =cut
- say "lst equal to lst2"
- if @lst == @$lst2; # 如果s 和s2 值相同
- say "lst and lst2 is the same"
- if \@lst == $lst2; # 如果s 和s2 是指向同一個對象的引用
- say "lst and lst3 has same type"
- if ref \@lst eq ref \@lst3; # 如果s 和s3 類型相同
5. Perl 中的“上下文”
上下文的判斷幾乎已經成了一名Perl 用戶不可或缺的第六感,一個顯而易見的例子是當你定義一個列表
- my @lst = (1..3);
當你循環遍歷的時候,你深知把for 循環
- say for @lst; # 列表上下文
替換為While 循環
- say while @lst; # 標量上下文
究竟會得到什么災難性的后果。
但是在Python 中你不必擔心這個:Python 對於數據類型的解釋很明確,不會因為上下文環境不同而改變,當你定義一個列表
- lst = [1, 2, 3]
之后,你永遠不必擔心在什么特殊的上下文中lst 會被Python 解釋為數值3。
面向對象編程
1. 幾點明顯的異同
Perl 和Python 的面向對象給人的感覺是后者明顯比前者更加規范,下面是自己感受最明顯的幾點異同。
a. 真的是面向對象么?
1). Perl 中的“面向對象”更像是“面向過程”的文字游戲:類通過包實現、方法通過包中定義的函數實現、類的繼承和方法的重載通過@ISA 列表查找循序實現、私有方法通過指向匿名函數的my 引用實現(因為my變量是本文件可見的)、運算符重載通過指向函數的引用實現、對象中成員變量的訪問通過bless 到類名的引用實現……這樣如果說有好處,那就是編程可以非常靈活——你可以用一個父類的多個子類共同繼承出一個類(例如從哺乳動物中人類和貓類繼承出一種新生物),Perl 完全對這種行為不在意,只要你確信這種近親結婚的方式真的是你需要達到的目的。當然壞處顯而易見——你可以輕而易舉把代碼寫的糟糕透頂。所以Perl 的“面向對象”給人的感覺只是“面向過程”的另一種玩法——面向過程的本質沒變,但是面向對象的效果達到了。
2). Python 中的“面向對象”比Perl 要嚴謹和規范許多,非常類似於Java,如果你熟悉Java 或者C++,那么你會很好理解Python 的面向對象編程。
b. 包和類
1). Perl 中兩者完全等價,一個包就是一個類(pm 是Perl 模塊的意思,但是它又被叫做包,而包就是類的意思 ← ←)。
2). Python 中一個包可以包含多個模塊,一個模塊可以包含多個類。
c. 靜態方法
Perl 和Python 中靜態方法都是第一個參數不是類的引用的方法,但是稍有不同:
1). Perl 中靜態方法第一個參數是類名,可以通過bless 新的引用到類名來操作對象類型(例如你在構造方法里做的那樣)。
2). Python 中靜態方法完全無法操作對象類型。
d. 私有方法:
1). Perl 中的私有方法通過my 變量只有當前文件可見的性質,用保存匿名函數的my 引用來達到“私有”的目的(“面向對象”的文字游戲)。
2). Python 中吧以“__”開頭的方法都當作私有方法,通過方法名會變成"_類名__方法名" 的形式來避免其他類調用該方法,但是你仍然可以通過手動變換后的方法名直接調用私有方法。
e. 方法的傳參:
1). Perl 中一般將散列的引用bless 到類名,所以傳參可以十分靈活,如果構造函數允許,參數個數和位置根本無關緊要,但是隨之造成的問題就是可能引發混亂。
2). Python 中方法聲明無法把無默認值的參數放在有默認值的參數后面,但是因為實參可以通過給出參數名手動顯式指定,所以次序也可以無關緊要。
f. 運算符重載:
1). Perl 通過use overload 模塊指定方法的引用來達到重載運算符的目的。
2). Python 中通過一組特殊名稱的方法來重載運算符。
g. 父類方法重載:
1). Perl 中通過@ISA 列表的搜索順序來達到重載父類方法的目的(子類的同名方法會被優先搜索到),並且可以顯式SUPER 偽類訪問被覆蓋的基類方法(就如你經常在析構方法中做的一樣)。
2). Python 的重載更加正式,形式非常類似於C++。
h. 繼承:
1). Perl 的繼承只是操作了@ISA 列表,子類中沒有的方法會在@ISA 中尋找方法名,因此這種行為得到的結果和面向對象編程的繼承相同。UNIVERSAL 是所有類的祖先類,提供了isa 方法用來測試繼承關系。
2). Python 的繼承類似於C++,顯式指定了要繼承的父類,object 類是所有類的祖先類,提供issubclass 方法用來測試繼承關系。
2. 一個演示異同的例子
下面的兩個例子都會有相同的輸出,演示了Perl 和Python 中類的構造、析構、公有方法、私有方法、運算符重載、繼承、父類方法重載等。
下面是預期的輸出
- =My name's Lucy, 2 years old. Adoption me please.
- +I am hungry offen.
- -My name's Leia, 1 years old. My host is iSpeller.
- +I hate milk but my host give me offen.
- -My name's Lucy, 2 years old. My host is iSpeller.
- +I hate milk but my host give me offen.
--------------------------------------------------------
下面是你熟悉的Perl
- #!/usr/bin/perl
- # ========================
- # filename: main.pm
- # main 類,演示了:
- # 類的實例化
- # =======================
- package main;
- use warnings;
- use strict;
- use 5.010;
- use Dog;
- use Pet_Dog;
- push @INC, '.';
- # 一條叫Lucy 的汪星人
- my $lucy = Dog->new (name => 'Lucy', age => 2);
- $lucy->say_hello;
- $lucy->my_secret;
- # 第一條寵物汪,默認為1 歲的leia
- my $pet_leia = Pet_Dog->new (host => 'iSpeller');
- $pet_leia->say_hello;
- $pet_leia->my_secret;
- # 納入第二個寵物汪
- # 調用了Pet 類運算符重載函數
- my $pet_lucy = $lucy + "iSpeller";
- $pet_lucy->say_hello;
- $pet_lucy->my_secret;
- 1;
- # ========================
- # filename: Dog.pm
- # Pet 類,演示了:
- # 構造、析構方法
- # 公有、私有方法
- # 重載
- # ========================
- package Dog;
- use strict;
- use warnings;
- use 5.010;
- use overload '+' => \&meet; # 重載加號運算符
- # 構造方法
- # 是靜態方法,第一個參數為類名
- sub new {
- my $type = shift;
- my $class = ref $type || $type;
- # 如有用戶實例變量則覆蓋默認屬性
- my $self = { name => 'Leia', age => 1, is_pet => 0, @_ };
- bless $self, $class;
- return $self;
- }
- # 析構方法
- # 是虛方法,第一個參數為類的引用
- sub DESTROY {
- my $self = shift;
- # 調用父類析構方法
- $self->SUPER::DESTROY
- if $self->can ('SUPER::DESTROY');
- }
- # 公有方法
- sub say_hello {
- my $self = shift;
- print '='
- if $self->isa ("UNIVERSAL"); # UNIVERSAL 類是所有類的祖先類
- printf "My name's %s, %d years old. %s.\n",
- $self->{name}, $self->{age},
- $self->{is_pet}
- ? "I am a pet dot"
- : "Adoption me please";
- }
- # 私有方法
- my $say_secret = sub {
- my $self = shift;
- say '+', $self->{is_pet}
- ? "I hate milk but my host give me offen."
- : "I am hungry offen.";
- };
- # 私有方法只能在本文件內由其他方法訪問(my $say_secret)
- sub my_secret {
- my $self = shift;
- $self->$say_secret;
- }
- # 重載加號運算符,返回成為寵物后的自身
- sub meet {
- my $self = shift;
- my @property = %$self;
- my $new = Pet_Dog->new (@property, host => shift);
- return $new;
- }
- 1;
- # ========================
- # filename: Pet_Dog.pm
- # Pet_Dog 類,繼承自Dog 類,演示了:
- # 繼承、父類方法的重載
- # =======================
- package Pet_Dog;
- use strict;
- use warnings;
- use 5.010;
- use base qw/Dog/; # 繼承自Dog 類
- sub new {
- # 調用父類的構造方法
- # 因為shift 得到的是子類的類名,所以不需要重新bless
- my $self = Dog::new (shift, host => "none", @_, is_pet => 1);
- return $self;
- }
- # 重載父類的say_hello (虛)方法
- sub say_hello {
- my $self = shift;
- print '-'
- if $self->isa ("Dog"); # 繼承關系測試
- printf "My name's %s, %d years old. My host is %s.\n",
- $self->{name}, $self->{age}, $self->{host};
- }
- 1;
--------------------------------------------------------
下面是完全等價的Python
- #!/usr/bin/python3
- # filename: main.py
- # ========================
- # Dog 類,演示了:
- # 構造、析構方法
- # 公有、私有方法
- # 重載
- # ========================
- class Dog:
- # 構造方法
- # 是私有方法,因為方法名以"__" 開頭
- def __init__ (self, name = "Leia", age = 1, is_pet = 0):
- # 如有用戶實例變量則覆蓋默認屬性
- self.name = name
- self.age = age
- self.is_pet = is_pet
- # 析構方法
- # 靜態方法,不會操作實例類型
- # 類似Perl,第一個參數不是引用,所以你無法通過第一個參數來引用實例變量
- @staticmethod
- def __del__ ():
- pass
- # 公有方法
- def say_hello (self):
- if issubclass (Dog, object):
- print ("=", end='')
- print ("My name's %s, %d years old. %s."
- % (self.name, self.age,
- # Python 中沒有三目運算符,可以用下面的形式替代
- "I am a pet dog"
- if self.is_pet
- else "Adoption me please"))
- # 私有方法
- def __say_secret (self):
- print ("+%s."
- % ("I hate milk but my host give me offen"
- if self.is_pet
- else "I am hungry offen"))
- # 私有方法只能在本類內由其他方法訪問
- def my_secret (self):
- self.__say_secret ()
- # 重載加號運算符為和對方戀愛,返回成為女朋友后的自身
- def __add__ (self, other):
- new = Pet_Dog (self.name, self.age, 1, other)
- return (new)
- # ========================
- # Pet_Dog 類,繼承自Dog 類,演示了:
- # 繼承、父類方法的重載
- # ========================
- class Pet_Dog (Dog):
- # 調用父類的構造方法
- # 之后初始化子類變量
- def __init__ (self, name = "Leia", age = 1, is_pet = 1, host = "none"):
- Dog.__init__ (self, name, age, is_pet)
- self.host = host
- # 重載父類的say_hello (虛)方法
- def say_hello (self):
- if issubclass (Pet_Dog, Dog):
- print ("-", end='')
- print ("My name's %s, %d years old. My host is %s."
- % (self.name, self.age, self.host))
- '''''
- 程序開始,Python 的類型不允許在定義之前使用
- 然而Python 似乎又不區分聲明和定義
- 演示了類的實例化
- '''
- # 一條叫Lucy 的汪星人
- lucy = Dog ("Lucy", 2)
- lucy.say_hello ()
- lucy.my_secret ()
- # 第一條寵物汪,默認為1 歲的leia
- pet_dog_leia = Pet_Dog (host = "iSpeller");
- pet_dog_leia.say_hello ()
- pet_dog_leia.my_secret ()
- # 納入第二寵物汪
- # 調用了Pet 類運算符重載函數
- pet_dog_lucy = lucy + "iSpeller"
- pet_dog_lucy.say_hello ()
- pet_dog_lucy.my_secret ()