從C#到Python —— 3 函數及函數編程

来源:http://www.cnblogs.com/shouce/archive/2016/03/14/5274481.html
-Advertisement-
Play Games

在C#中沒有獨立的函數存在,只有類的(動態或靜態)方法這一概念,它指的是類中用於執行計算或其它行為的成員。在Python中,你可以使用類似C#的方式定義類的動態或靜態成員方法,因為它與C#一樣支持完全的面向對象編程。你也可以用過程式編程的方式來編寫Python程式,這時Python中的函數與類可以沒


在C#中沒有獨立的函數存在,只有類的(動態或靜態)方法這一概念,它指的是類中用於執行計算或其它行為的成員。在Python中,你可以使用類似C#的方式定義類的動態或靜態成員方法,因為它與C#一樣支持完全的面向對象編程。你也可以用過程式編程的方式來編寫Python程式,這時Python中的函數與類可以沒有任何關係,類似C語言定義和使用函數的方式。此外,Python還支持函數式編程,雖然它對函數式編程的支持不如LISP等語言那樣完備,但適當使用還是可以提高我們工作的效率。

本章主要介紹在過程編程模式下Python中函數的定義和使用方法,關於在面向對象編程中如何使用函數,我們將在下一章再討論。此外,我還會簡要介紹Python中的函數編程功能。

3.1  函數的定義

函數定義是最基本的行為抽象代碼,也是軟體復用最初級的方式。Python中函數的定義語句由def關鍵字、函數名、括弧、參數(可選)及冒號:組成。下麵是幾個簡單的函數定義語句:

複製代碼
 1 # -*- coding: utf-8 -*-
2 #定義沒有參數、也沒有返回值的函數
3  def F1():
4 print 'hello kitty!'
5  #定義有參數和一個返回值的函數
6  def F2(x,y):
7 a = x + y
8 return a
9  #定義有多個返回值的函數,用逗號分割不同的返回值,返回結果是一個元組
10  def F3(x,y):
11 a = x/y
12 b = x%y
13 return a,b
複製代碼

可能你已經註意到了,Python定義函數的時候並沒有約束參數的類型,它以最簡單的形式支持了泛型編程。你可以輸入任意類型的數據作為參數,只要這些類型支持函數內部的操作(當然必要時需要在函數內部做一些類型判斷、異常處理之類的工作)。

3.2  函數的參數

3.2.1  C#與Python在函數參數定義方面的差別

C#中方法的參數有四種類型:

(1) 值參數不含任何修飾符
(2) 引用型參數以ref 修飾符聲明(Python中沒有對應的定義方式)
(3) 輸出參數以out 修飾符聲明(Python中不需要,因為函數可以有多個返回值)
(4) 數組型參數以params 修飾符聲明

Python中函數參數的形式也有四種類型:

(1) f(arg1,arg2,...) 這是最常用的函數定義方式
(2) f(arg1=value1,arg2=value2,...,argN=valueN) 這種方式為參數提供了預設值,同時在調用函數時參數順序可以變化,也稱為關鍵字參數。
(3) f(*arg) arg代表了一個tuple,類似C#中的params修飾符作用,可以接受多個參數
(4) f(**arg) 傳入的參數在函數內部是保存在名稱為arg的dict中,調用的時候需要使用如f(a1=v1,a2=v2)的形式。

可以看出,Python函數參數定義與C#相比,最大的兩個區別是支持關鍵字參數和字典參數。

 

3.29更新:根據JeffreyZhao提示,C# 4.0 已經支持命名參數(即關鍵字參數)和可選參數(即參數預設值),詳情可見生魚片blog上的:《C#4.0新特性:可選參數,命名參數,Dynamic》。在此向兩位致謝。

 

3.2.2  關鍵字參數

關鍵字參數可以使我們調用含有很多參數、同時一些參數具有預設值的Python函數變得更簡單,也是Python實現函數重載的一種重要手段(到類與對象部分再詳細說這個問題)。下麵的例子說明瞭如何定義和調用含關鍵字參數的函數(引自Guido van Rossum《Python入門》,包括後面的幾個例子):

1 def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
2 print "-- This parrot wouldn't", action,
3 print "if you put", voltage, "Volts through it."
4 print "-- Lovely plumage, the", type
5 print "-- It's", state, "!"


可以用如下幾種方式調用:

1 parrot(1000)                        # 預設值
2  parrot(action = 'VOOOOOM', voltage = 1000000) # 關鍵字,預設值,次序可變
3  parrot('a thousand', state = 'pushing up the daisies')# 位置參數,預設值,關鍵字
4  parrot('a million', 'bereft of life', 'jump') # 位置參數,預設值


但以下幾種調用方式是錯誤的:

1 parrot()                     # 非預設的參數沒有提供
2  parrot(voltage=5.0, 'dead') # 關鍵字參數後面又出現了非關鍵字參數
3  parrot(110, voltage=220) # 參數值重覆提供
4  parrot(actor='John Cleese') # 未知關鍵字

3.2.3  字典參數

如果形參表中有一個形為**name的形參,在調用時這個形參可以接收一個字典,字典中包含所有不與任何形參匹配的關鍵字參數。例如下麵的函數:

1 def cheeseshop(**keywords):
2 for kw in keywords.keys(): print kw, ':', keywords[kw]


就可以象下麵這樣調用:

1 cheeseshop(client='John Cleese',
2 shopkeeper='Michael Palin',
3 sketch='Cheese Shop Sketch')


結果顯示:

client : John Cleese
shopkeeper : Michael Palin
sketch : Cheese Shop Sketch

3.2.4   函數參數調用的順序

調用Python函數時,參數書寫的順序分別為:非關鍵字參數,關鍵字參數,元組參數,字典參數。請記住以下幾點規則:

    * 通過位置分配非關鍵字參數
    * 通過匹配變數名分配關鍵字參數
    * 其他額外的非關鍵字參數分配到*name元組中
    * 其他額外的關鍵字參數分配到**name的字典中
    * 用預設值分配給在調用時未得到分配的參數

一般說來,實參表中非關鍵字參數在前,關鍵字參數在後,關鍵字名字必須是形參名字。形參有沒有預設值都可以用關鍵字參數的形式調用。每一形參至多只能對應一個實參,因此,已經由位置參數傳入值的形參就不能在同一調用中再作為關鍵字參數。

總之,由於Python的函數參數定義和調用方式太靈活了,所以一開始容易把人搞暈。不過可以慢慢來,你會越來越發現Python的簡便所在。

3.3  函數文檔

在C#,可以使用文檔XML標記對函數進行註釋,這樣在VS等IDE中,輸入函數名後就會提示函數的功能、參數及返回值等的說明,方便函數的調用者。在Python中,也有類似的功能,即文檔字元串(Docstrings)。但Python的文檔字元串不像C#中的文檔XML標記那麼豐富,基本等同於C#中的<summary>標記,下麵讓我們一起來通過一個例子瞭解一下(引自《簡明Python教程》):

複製代碼
 1     def printMax(x, y):
2 '''Prints the maximum of two numbers.
3
4 The two values must be integers.'''
5 x = int(x) # convert to integers, if possible
6   y = int(y)
7
8 if x > y:
9 print x, 'is maximum'
10 else:
11 print y, 'is maximum'
12
13 printMax(3, 5)
14 print printMax.__doc__
複製代碼

    輸出

    $ python func_doc.py
    5 is maximum
    Prints the maximum of two numbers.

            The two values must be integers.

在上述函數中,第一個邏輯行的字元串是這個函數的文檔字元串。文檔字元串的慣例是一個多行字元串,它的首行以大寫字母開始,句號結尾。第二行是空行,從第三行開始是詳細的描述,描述對象的調用方法、參數說明、返回值等具體信息。

可以使用__doc__(註意雙下劃線)調用printMax函數的文檔字元串屬性(屬於函數的名稱,請記住Python把每一樣東西都作為對象,包括這個函數)。它等同於用Python的內建函數help()讀取函數的說明。很多Python的IDE也依賴於函數的文檔字元串進行代碼的智能提示,因此我們在編寫函數時應養成編寫文檔字元串的習慣。

3.4  函數編程

Python中加入了一些在函數編程語言和Lisp中常見的功能,如匿名函數、高階函數及列表內涵等,關於最後一個問題我在《2 運算符、表達式和流程式控制制》已經介紹過了,本章只介紹匿名函數和高階函數。

3.4.1  匿名函數

lambda函數是匿名函數,用來定義沒有名字的函數對象。在Python 中,lambda只能包含表達式:lambda arg1, arg2 ... : expression。lambda 關鍵字後就是逗號分隔的形參列表,冒號後面是一個表達式,表達式求值的結果為lambda的返回值。

雖然lambda的濫用會嚴重影響代碼可讀性,不過在適當的時候使用一下lambda 來減少鍵盤的敲擊還是有其實際意義的,比如做排序的時候,使用data.sort(key=lambda o:o.year)顯然比

1 def get_year(o):
2 return o.year
3 func=get_year(o)
4 data.sort(key=func)

要方便許多。(引自《可愛的Python》)

 

3.29更新:根據JeffreyZhao提示,重新整理C#中匿名函數的內容(本部分主要參考MSDN,見http://msdn.microsoft.com/zh-cn/library/bb882516.aspx):

在C#中也有匿名函數功能。在 C# 1.0 中,通過使用在代碼中其他位置定義的方法顯式初始化委托來創建委托的實例。C# 2.0引入了匿名方法的概念,作為一種編寫可在委托調用中執行的未命名內聯語句塊的方式。C# 3.0 引入了 Lambda表達式,這種表達式與匿名方法的概念類似,但更具表現力並且更簡練。這兩個功能統稱為“匿名函數”。通常,針對 .NET 3.5 及更高版本的應用程式應使用 Lambda 表達式。

 Lambda 表達式可以包含表達式和語句,並且可用於創建委托或表達式目錄樹類型。所有 Lambda 表達式都使用 Lambda 運算符 =>, 運算符的左邊是輸入參數(如果有),右邊包含表達式或語句塊。可以將此表達式分配給委托類型,如下所示:

 

1 delegate int del(int i);
2 del myDelegate = x => x * x;
3  int j = myDelegate(5); //j = 25

 

3.4.2  高階函數

高階函數(High Order)是函數式編程的基礎,常用的高階函數有:
(1) 映射,也就是將演算法施於容器中的每個元素,將返回值合併為一個新的容器。
(2) 過濾,將演算法施於容器中的每個元素,將返回值為真的元素合併為一個新的容器。
(3) 合併,將演算法(可能攜帶一個初值)依次施於容器中的每個元素,將返回值作為下一步計算的參數之一,與下一個元素再計算,直至最終獲得一個總的結果。

Python通過map、filter、reduce三個內置函數來實現上述三類高階函數功能。本文不對這三個函數的用法進行詳細介紹,僅給出它們使用的示例代碼(引自《Python的map、filter、reduce函數》),請細細體會:

複製代碼
 1 
2  #coding:utf-8
3  
4  def map_func(lis):
5 return lis + 1
6
7  def filter_func(li):
8 if li % 2 == 0:
9 return True
10 else:
11 return False
12
13  def reduce_func(li, lis):
14 return li + lis
15
16 li = [1,2,3,4,5]
17
18 map_l = map(map_func, li) #將li中所有的數都+1
19  filter_l = filter(filter_func, li) #得到li中能被2整除的
20  reduce_l = reduce(reduce_func, li) #1+2+3+4+5
21  
22  print map_l
23  print filter_l
24  print reduce_l
複製代碼

運行結果如下:
C:\>python test1.py
[2, 3, 4, 5, 6]
[2, 4]
15

3.29更新:根據JeffreyZhao提示,C#可以用委托實現函數編程的大部分功能。不過說實話我沒這麼用過,因為是接觸了Python後才有的函數編程概念,以前用C#寫程式就壓根沒有函數編程思想,雖然知道"委托允許將方法作為參數進行傳遞"。但具體細節估計各位C#高手比我要清楚得多,我就不亂寫了。

 

3.5  小結

本章討論了Python中函數的定義和使用方法,要點如下:

(1) Python用def關鍵字、函數名、括弧、參數(可選)及冒號定義函數(要記得函數體縮進呀),函數可以有0、1或多個返回值;
(2) Python定義函數不需要(也不能)指定參數類型,它天生就是泛型的;
(3) Python支持(單獨或同時使用)普通參數、關鍵字參數、元組參數和字典參數四種類型的參數,請學會靈活使用它們;
(4) 文檔字元串(Docstrings)用於對Python的函數提供註釋,供自省函數及IDE調用;
(5) lambda關鍵字用來定義匿名函數,它僅支持表達式,某些情況下可以簡化代碼編寫量,但不要濫用;
(6) Python通過map、filter、reduce三個內置函數實現函數編程中映射、過濾及合併三類高階函數功能,但註意map和filter可以用列表內涵替代(參見第2章)。

 

進一步閱讀的參考:

[1] 本章內容較多的參考了Python之父Guido van Rossum的《Python入門》以及國內的一本經典教程《可愛的Python》,再次向大家推薦這兩本書,在此對作者及譯者致謝。

[2] 限於篇幅和深度,關於Python函數中的一些重要主題沒有討論,例如作用域、函數嵌套、遞歸等,我的觀點是先不要把事情弄得太複雜,慢慢來總是不錯的。如果你已學會了本章的內容,推薦你進一步閱讀《深入Python》,這本書中對很多主題的討論都是(我看多的書中)最深入的。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 年後回來,跟之前和幾個同事和朋友聊天,發現有兩個.net的和一個php的朋友都轉到了前端,真是出乎意料。自從之前的webapp興起後,前端感覺比後端吃香很多,總結朋友們轉的原因,大概就幾點 1.易上手,相對其他語言來說,作為後端人員,轉到前端,其實已經有了很好的底子和基礎了,畢竟以前多少都會和js,
  •   使用C#實現加減乘除演算法經常被用作新手練習。本篇來分別體驗通過委托、介面、匿名方法、泛型委托來實現。 加減乘除擁有相同的參數個數、類型和返回類型,首先想到了使用委托實現。     以上,委托用在了方法層面。如果在類層面,也可用介面封裝加減乘除的共性。     委托還可以結合匿名方法一起使用。  
  • 角色是網站中都有的一個功能,用來區分用戶的類型、劃分用戶的許可權,這次實現角色列表瀏覽、角色添加、角色修改和角色刪除。 目錄 奔跑吧,代碼小哥! MVC5網站開發之一 總體概述 MVC5 網站開發之二 創建項目 MVC5 網站開發之三 數據存儲層功能實現 MVC5 網站開發之四 業務邏輯層的架構和基本...
  • WebApi2上進行依賴註入,在百度里能搜到的的完整解決方案的文章少之又少,缺胳膊斷腿。 和MVC5依賴註入的不同之處,並且需要註意的地方,標記在註釋當中。上Global代碼: 也沒有太多需要解釋的地方,Controller中還是構造器註入。開發中已經親測有效。    可以收藏,以後查看。  
  • 自從上次分享《Redis到底該如何利用?》已經有1年多了,這1年經歷了不少。從碼了我們網站的第一行開始到現在,我們的緩存模塊也不斷在升級,這之中確實略有心得,最近也有朋友探討緩存,覺得可以總結下分享下拙見,期待能有更深入的研究。 緩存是什麼? 我時常在群里或者在社區里看到有人對緩存有諸多疑問,搞不清
  •   在函數式編程中,可以把函數看作數據。函數也可以作為參數,函數還可以返回函數。比如,LINQ就是基於函數式編程的。 語句式編程可能這樣寫:   而使用函數式表達式,可以簡化為:   再來看一個過濾和排序的例子:   函數式編程可以寫成如下:   或   可見,在LINQ中,一個表達式(函數)的返回
  • 以下是 .NET Framework 4.5 中 ADO.NET 的新增功能。 以下是 .NET Framework 4.5 中用於 SQL Server 的 .NET Framework 數據提供程式的新增功能: ConnectRetryCount 和 ConnectRetryInterval 連
  • PHP,是英文超級文本預處理語言Hypertext Preprocessor的縮寫。PHP 是一種 HTML 內嵌式的語言,是一種在伺服器端執行的嵌入HTML文檔的腳本語言,語言的風格有類似於C語言,被廣泛的運用。自從1994年PHP語言的創建,神奇般的被追捧為網站設計的首選語言。2000年PHP4
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...