Perl 和 Python 的比較 【轉】


 

轉自: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 中習以為常的操作

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. my ($string, $number) = ("1", );  
  2. $number = $string + "1";  # $string 和"1" 都被解釋為數值  
  3. say $number;  
  4. $string = $number.1;      # $number 和1 都被解釋為字符串  
  5. say $string;  

 

但是Python 中你必須顯式的指定數據類型

 

[python]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. string = "1"  
  2. number = int (string) + 1     # 必須顯式轉換為數值類型  
  3. print (number)  
  4. string = str (number) + "1"   # 同樣必須顯式轉換為字符串  
  5. print (string)  

 

 

b. Perl 的標量不支持下標運算,但是Python 的字符串可以

Python 中你可以很方便的用下標索引字符串(而且和Perl 中的列表一樣也支持用負數做反向索引)

 

[python]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. s = "A string"  
  2. print (s[2:])  

但是Perl 的標量就無法這樣操作,相同的操作必須用列表切片完成

 

 

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. $_ = "A string";  
  2. say ((split //)[2..split //]);  

 

c. Perl 中的heredoc 式標量賦值在Python 中可以使用三引號運算符完成

 

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. @_ = <<end;  < span="" style="word-wrap: break-word;">
  2. Everthing is  
  3.     in  
  4.       
  5. the array  
  6. END  
  7. say "@_";  

下面是等價的Python 代碼,注意第一行是緊跟三引號運算符之后的

 

 

[python]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. string = '''''Everthing is 
  2.     in 
  3.      
  4. the array 
  5. '''  
  6. print (string)  

 

 

2. Perl 中的列表

a. Perl 中的列表Python 中對應為列表和元組

下面是Python 中倒序排序一個列表的操作

 

[python]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. my_list = ["Hello""Python"]  
  2. my_list.append ("World")  
  3. my_list += ["!"]  
  4. print (my_list)  
  5. my_list.sort ();  
  6. my_list.reverse ();  
  7. for string in my_list:  
  8.   print (string)  

對應的Perl 同性質操作的版本

 

 

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. @_ = qw/Hello Perl/;  
  2. push @_, 'World';  
  3. @_ = (@_, '!');  
  4. print "@_\n";  
  5. @_ = reverse sort @_;  
  6. say for @_;  

 

 

b. 常用的splice 操作在Python 中可以用過index () 方法和del 操作完成

下面是Perl 中常用的splice 操作

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. @_ = qw/Hello Perl !/;  
  2. splice @_, 2, 0, "World";       # 插入元素  
  3. say "@_";  
  4. my @removed = splice @_, 1, 2;  # 提取一段元素並在原列表刪除  
  5. print "@_\n@removed\n";  

Python 中對應的操作

 

 

[python]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. my_list = ["Hello""Python""!"]  
  2. my_list.insert (2"World"# 插入元素  
  3. print (my_list)  
  4. removed = my_list[1:2]      # 提取一段元素  
  5. del my_list[1:2]            # 並在原列表刪除  
  6. print (my_list)  
  7. print (removed)  

 

注意:Python 中元組和列表的區別

元組創建后是只讀的,但是列表隨時都可以被修改。

 

3. Perl 中的哈希

a. Perl 中的哈希Python 中對應為字典和集合

一個Perl 哈希按照鍵值升序遍歷的操作如下

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. my %hash = (  
  2.   "first" => 1,  
  3.   "second" => 2,  
  4.   "third" => 3  
  5. );  
  6. say "$_ => $hash{$_}" for sort keys %hash;  

Python 中代碼如下(注意定義變為花括號)

 

 

[python]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. my_hash = {  
  2.   "first" : 1,  
  3.   "second" : 2,  
  4.   "third" : 3  
  5. }  
  6. items =  sorted (my_hash.items (), key=lambda e:e[1], reverse=False)  
  7. for item in items:  
  8.   print (item[0], ':', item[1])  

 

 

注意:Python 中集合與字典的不同之處

(a) 僅包含鍵,但是沒有值

(b) set 可變,但是frozenset 不可變(如其名)

 

4. Python 強類型的判斷和賦值

一個非常大的不同之處:Perl 中賦值號默認的行為是拷貝,但是Python 中賦值號會創建一個對象的引用。

另外,因為和弱類型的Perl 不同,Python 是一門強類型語言,所以也有判斷對象類型的必要。

下面代碼演示了這些區別,首先是一段Python 代碼

 

[python]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. lst = [123]  
  2. lst2 = lst                  # lst2 是lst 的一個引用  
  3. print (lst)  
  4. lst2[1] = 2.5               # 所以修改lst2 也會引起lst 的變化  
  5. print (lst)  
  6. lst3 = copy.deepcopy (lst)  # lst3 是lst 的一個深拷貝  
  7. lst3[1] = 2  
  8. print (lst)                 # 修改lst3 不會引起lst 的變化  
  9.   
  10. # 因為Python 是強類型語言,所以需要類型判斷操作  
  11. if lst == lst2:                 # 如果s 和s2 值相同  
  12.   print ("lst equal to lst2")  
  13. if lst is lst2:                 # 如果s 和s2 是指向同一個對象的引用  
  14.   print ("lst and lst2 is the same")  
  15. if type (lst) is type (lst3):   # 如果s 和s3 類型相同  
  16.   print ("lst and lst3 has same type")  

下面是與之完全等價的Perl 代碼,對比一下你就會很快了解其中的差別

 

 

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. my @lst = (1..3);  
  2. my $lst2 = \@lst;   # lst2 是lst 的一個引用  
  3. say "@$lst2";  
  4. $lst2->[1] = 2.5;   # 所以修改lst2 也會引起lst 的變化  
  5. say "@$lst2";  
  6. my @lst3 = @lst;    # lst3 是lst 的一個深拷貝  
  7. $lst3[1] = 2;  
  8. say "@lst";         # 修改lst3 不會引起lst 的變化  
  9.   
  10. =pod  
  11. 因為Perl 是弱類型語言,所以不需要類型判斷操作  
  12. 但是Perl 中仍然可以做這樣的操作——雖然沒什么意義  
  13. =cut  
  14. say "lst equal to lst2"  
  15.   if @lst == @$lst2;          # 如果s 和s2 值相同  
  16. say "lst and lst2 is the same"  
  17.   if \@lst == $lst2;          # 如果s 和s2 是指向同一個對象的引用  
  18. say "lst and lst3 has same type"  
  19.   if ref \@lst eq ref \@lst3; # 如果s 和s3 類型相同  

 

5. Perl 中的“上下文”

上下文的判斷幾乎已經成了一名Perl 用戶不可或缺的第六感,一個顯而易見的例子是當你定義一個列表

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. my @lst = (1..3);  

當你循環遍歷的時候,你深知把for 循環

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. say for @lst;   # 列表上下文  

替換為While 循環

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. say while @lst; # 標量上下文  

究竟會得到什么災難性的后果。

 

但是在Python 中你不必擔心這個:Python 對於數據類型的解釋很明確,不會因為上下文環境不同而改變,當你定義一個列表

[python]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. lst = [123]  

之后,你永遠不必擔心在什么特殊的上下文中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 plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. =My name's Lucy, 2 years old. Adoption me please.  
  2. +I am hungry offen.  
  3. -My name's Leia, 1 years old. My host is iSpeller.  
  4. +I hate milk but my host give me offen.  
  5. -My name's Lucy, 2 years old. My host is iSpeller.  
  6. +I hate milk but my host give me offen.  


--------------------------------------------------------

下面是你熟悉的Perl

 

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. #!/usr/bin/perl  
  2.   
  3. # ========================  
  4. # filename: main.pm  
  5. # main 類,演示了:  
  6. # 類的實例化  
  7. # =======================  
  8.   
  9. package main;  
  10. use warnings;  
  11. use strict;  
  12. use 5.010;  
  13. use Dog;  
  14. use Pet_Dog;  
  15.   
  16. push @INC, '.';  
  17.   
  18. # 一條叫Lucy 的汪星人  
  19. my $lucy = Dog->new (name => 'Lucy', age => 2);  
  20. $lucy->say_hello;  
  21. $lucy->my_secret;  
  22.   
  23. # 第一條寵物汪,默認為1 歲的leia  
  24. my $pet_leia = Pet_Dog->new (host => 'iSpeller');  
  25. $pet_leia->say_hello;  
  26. $pet_leia->my_secret;  
  27.   
  28. # 納入第二個寵物汪  
  29. # 調用了Pet 類運算符重載函數  
  30. my $pet_lucy = $lucy + "iSpeller";  
  31. $pet_lucy->say_hello;  
  32. $pet_lucy->my_secret;  
  33.   
  34. 1;  

 

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. # ========================  
  2. # filename: Dog.pm  
  3. # Pet 類,演示了:  
  4. # 構造、析構方法  
  5. # 公有、私有方法  
  6. # 重載  
  7. # ========================  
  8.   
  9. package Dog;  
  10. use strict;  
  11. use warnings;  
  12. use 5.010;  
  13.   
  14. use overload '+' => \&meet;   # 重載加號運算符  
  15.   
  16. # 構造方法  
  17. # 是靜態方法,第一個參數為類名  
  18. sub new {  
  19.   my $type = shift;  
  20.   my $class = ref $type || $type;  
  21.   
  22.   # 如有用戶實例變量則覆蓋默認屬性  
  23.   my $self = { name => 'Leia', age => 1, is_pet => 0, @_ };  
  24.   bless $self, $class;  
  25.   return $self;  
  26. }  
  27.   
  28. # 析構方法  
  29. # 是虛方法,第一個參數為類的引用  
  30. sub DESTROY {  
  31.   my $self = shift;  
  32.   
  33.   # 調用父類析構方法  
  34.   $self->SUPER::DESTROY  
  35.     if $self->can ('SUPER::DESTROY');  
  36. }  
  37.   
  38. # 公有方法  
  39. sub say_hello {  
  40.   my $self = shift;  
  41.   
  42.   print '='  
  43.     if $self->isa ("UNIVERSAL");  # UNIVERSAL 類是所有類的祖先類  
  44.   
  45.   printf "My name's %s, %d years old. %s.\n",  
  46.           $self->{name}, $self->{age},  
  47.           $self->{is_pet}  
  48.             ? "I am a pet dot"  
  49.             : "Adoption me please";  
  50. }  
  51.   
  52. # 私有方法  
  53. my $say_secret = sub {  
  54.   my $self = shift;  
  55.   
  56.   say '+', $self->{is_pet}  
  57.         ? "I hate milk but my host give me offen."  
  58.         : "I am hungry offen.";  
  59. };  
  60.   
  61. # 私有方法只能在本文件內由其他方法訪問(my $say_secret)  
  62. sub my_secret {  
  63.   my $self = shift;  
  64.   $self->$say_secret;  
  65. }  
  66.   
  67. # 重載加號運算符,返回成為寵物后的自身  
  68. sub meet {  
  69.   my $self = shift;  
  70.   my @property = %$self;  
  71.   my $new = Pet_Dog->new (@property, host => shift);  
  72.   return $new;  
  73. }  
  74.   
  75. 1;  

 

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. # ========================  
  2. # filename: Pet_Dog.pm  
  3. # Pet_Dog 類,繼承自Dog 類,演示了:  
  4. # 繼承、父類方法的重載  
  5. # =======================  
  6.   
  7. package Pet_Dog;  
  8. use strict;  
  9. use warnings;  
  10. use 5.010;  
  11.   
  12. use base qw/Dog/;  # 繼承自Dog 類  
  13.   
  14. sub new {  
  15.   # 調用父類的構造方法  
  16.   # 因為shift 得到的是子類的類名,所以不需要重新bless  
  17.   my $self = Dog::new (shift, host => "none", @_, is_pet => 1);  
  18.   return $self;  
  19. }  
  20.   
  21. # 重載父類的say_hello (虛)方法  
  22. sub say_hello {  
  23.   my $self = shift;  
  24.   print '-'  
  25.     if $self->isa ("Dog");   # 繼承關系測試  
  26.   
  27.   printf "My name's %s, %d years old. My host is %s.\n",  
  28.           $self->{name}, $self->{age}, $self->{host};  
  29. }  
  30.   
  31. 1;  



 

--------------------------------------------------------

下面是完全等價的Python

 

[python]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. #!/usr/bin/python3  
  2.   
  3. # filename: main.py  
  4.   
  5. # ========================  
  6. # Dog 類,演示了:  
  7. # 構造、析構方法  
  8. # 公有、私有方法  
  9. # 重載  
  10. # ========================  
  11. class Dog:  
  12.     # 構造方法  
  13.     # 是私有方法,因為方法名以"__" 開頭  
  14.     def __init__ (self, name = "Leia", age = 1, is_pet = 0):  
  15.         # 如有用戶實例變量則覆蓋默認屬性  
  16.         self.name = name  
  17.         self.age = age  
  18.         self.is_pet = is_pet  
  19.   
  20.     # 析構方法  
  21.     # 靜態方法,不會操作實例類型  
  22.     # 類似Perl,第一個參數不是引用,所以你無法通過第一個參數來引用實例變量  
  23.     @staticmethod  
  24.     def __del__ ():  
  25.         pass  
  26.   
  27.     # 公有方法  
  28.     def say_hello (self):  
  29.         if issubclass (Dog, object):  
  30.             print ("=", end='')  
  31.   
  32.         print ("My name's %s, %d years old. %s."  
  33.                 % (self.name, self.age,  
  34.                     # Python 中沒有三目運算符,可以用下面的形式替代  
  35.                     "I am a pet dog"  
  36.                         if self.is_pet  
  37.                     else "Adoption me please"))  
  38.   
  39.     # 私有方法  
  40.     def __say_secret (self):  
  41.         print ("+%s."  
  42.                 % ("I hate milk but my host give me offen"  
  43.                     if self.is_pet  
  44.                     else "I am hungry offen"))  
  45.   
  46.     # 私有方法只能在本類內由其他方法訪問  
  47.     def my_secret (self):  
  48.         self.__say_secret ()  
  49.   
  50.     # 重載加號運算符為和對方戀愛,返回成為女朋友后的自身  
  51.     def __add__ (self, other):  
  52.         new = Pet_Dog (self.name, self.age, 1, other)  
  53.         return (new)  
  54.   
  55. # ========================  
  56. # Pet_Dog 類,繼承自Dog 類,演示了:  
  57. # 繼承、父類方法的重載  
  58. # ========================  
  59. class Pet_Dog (Dog):  
  60.     # 調用父類的構造方法  
  61.     # 之后初始化子類變量  
  62.     def __init__ (self, name = "Leia", age = 1, is_pet = 1, host = "none"):  
  63.         Dog.__init__ (self, name, age, is_pet)  
  64.         self.host = host  
  65.   
  66.     # 重載父類的say_hello (虛)方法  
  67.     def say_hello (self):  
  68.         if issubclass (Pet_Dog, Dog):  
  69.             print ("-", end='')  
  70.   
  71.         print ("My name's %s, %d years old. My host is %s."  
  72.                 % (self.name, self.age, self.host))  
  73.   
  74. ''''' 
  75. 程序開始,Python 的類型不允許在定義之前使用 
  76. 然而Python 似乎又不區分聲明和定義 
  77. 演示了類的實例化 
  78. '''  
  79. # 一條叫Lucy 的汪星人  
  80. lucy = Dog ("Lucy"2)  
  81. lucy.say_hello ()  
  82. lucy.my_secret ()  
  83.   
  84. # 第一條寵物汪,默認為1 歲的leia  
  85. pet_dog_leia = Pet_Dog (host = "iSpeller");  
  86. pet_dog_leia.say_hello ()  
  87. pet_dog_leia.my_secret ()  
  88.   
  89. # 納入第二寵物汪  
  90. # 調用了Pet 類運算符重載函數  
  91. pet_dog_lucy = lucy + "iSpeller"  
  92. pet_dog_lucy.say_hello ()  
  93. pet_dog_lucy.my_secret () 


轉載自:http://blog.csdn.net/iSpeller/article/details/23198211


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM