Code:https://github.com/lotapp/BaseCode
多圖舊版:https://www.cnblogs.com/dunitian/p/9186561.html
在線編程:https://mybinder.org/v2/gh/lotapp/BaseCode/master
在線預覽:http://github.lesschina.com/python/base/pop/4.func.html
主要是普及一下Python基礎語法,對比着Net,新手更容易上手。
對比學習很有意思的,尤其是一些底層和思路
本來感覺函數要說的地方沒多少,細細一規划,發現~還是單獨拉出一篇說說吧
之后就進入面向對象了,函數還是有必要了解一下的,不然到時候Class里面的方法定義又要說了。
演示的模式一直在變,剛開始讓大家熟悉一下 VSCode
,后來引入了 ipython3
交互式編程的概念
現在就用前幾天講的 Jupyter-Notebook
來演示了(VSCode現在也有這個預覽版的插件了)
過幾天我們會直接 像寫文章一般的Coding,一邊寫文章一邊Code,最后還能生成需要的頁面
1.Python函數定義¶
1.1.定義一個無參函數¶
定義一個空函數:(不寫pass就報錯了)
# 空函數定義
def method():
pass #相當於占位符,ifelse等等里面都是可以出現的
method()
# 定義一個無參函數
def get_name():
print("我叫張三")
# 調用一個無參函數
get_name()
# 定義一個含參數的函數(name和age都是形參)
def show_infos(name,age):
"""打印name和age"""#函數文檔說明
print("我叫",name,"我的年齡是",age)#這種輸出方式會自動以空格連接字符串
# 調用一個含參數的函數
show_infos("李四",22)#實參
# 定義一個含默認參數(缺省參數)的函數(默認參數必須指向不變對象)
# 比如你設置一個list,改變了內容,則下次調用時,默認參數(缺省參數)的內容就變了,不再是函數定義時的值了
def default_param(name,age=23):
"""age默認為23"""
print("我叫",name,"我的年齡是",age)#這種輸出方式會自動以空格連接字符串
# 調用一個默認參數(缺省參數)的函數
default_param("張三")
# 定義有返回值的函數
def div_have_return(a,b):
"""計算a+b,返回計算結果"""#函數文檔說明
return a+b
# 調用有返回值的函數
result=div_have_return(1,2)
print("計算結果為",result)
# 定義含有多個返回值的函數(利用了元組)
def div_have_returns(a,b):
"""計算a+b的和,計算a-b,並返回兩個結果"""
return (a+b),(a-b)
# 調用含有多個返回值的函數
sum,dvalue=div_have_returns(1,2)
print("sum:",sum,"D-value:",dvalue)
# 測試一下~返回多個值其實是利用了元組
# 多返回值只是一種假象,Python函數返回的仍然是單一值~元組
test=div_have_returns(1,2)
print(test)
# 定義函數時,需要確定函數名和參數個數
# 如果有必要,可以先對參數的數據類型做檢查
# 函數體內部可以用return隨時返回函數結果
# 函數執行完畢也沒有return語句時,自動return None
# 函數可以同時返回多個值,但其實就是一個tuple
# 擴展:使用list實現
# 定義含有多個返回值的函數(利用了元組)
def div_have_returns(a,b):
"""計算a+b的和,計算a-b,並返回兩個結果"""
return [a+b,a-b]
# 調用含有多個返回值的函數
div_have_returns(1,2)
######## 通過元組、列表實現 ########
def default_some_params(nums):
"""借助Tuple和list"""
sum=0
for item in nums:
sum+=item
return sum
# 元組傳入
default_some_params((1,2,3,4,5))
# 列表傳入
default_some_params(list(range(1,6)))
2.2.可變參數¶
定義一個可變參數的函數(參數名字一般都是*args
)
######## 定義一個可變參數的函數(名字一般都是*args) ########
# 定義可變參數和定義一個list或tuple參數相比,僅僅在參數前面加了一個*號。
# 在函數內部,接收到的參數是一個tuple。調用該函數時,可以傳入任意個參數(包括0個參數)
def default_params(*args):
"""定義一個可變參數,用來求所有參數的總和"""
sum=0
for item in args:
sum+=item
return sum
# 調用一個可變參數的函數
default_params(1,2,3,4,5)
# 傳一個list或者Tuple(參數前面加*)
# 傳一個元組
test_tuple=(1,2,3,4,5)
default_params(*test_tuple)
# range(1,6) ==> [1,6) ==> 1,2,3,4,5
test_list=list(range(1,6))
default_params(*test_list)
來個Main傳參的擴展,貼兩張圖即可:
2.3.關鍵字參數¶
定義含關鍵字參數的函數:def default_kv_params(name,age=23,**kv):
可變參數允許你傳入0個或任意個參數,這些可變參數在函數調用時自動組裝為一個tuple
關鍵字參數允許你傳入0個或任意個含key-value
的參數,自動組裝為一個dict
######## 調用含關鍵字參數的函數 ########
# 可變參數允許你傳入0個或任意個參數,這些可變參數在函數調用時自動組裝為一個tuple
# 關鍵字參數允許你傳入0個或任意個含key-value的參數,自動組裝為一個dict
def default_kv_params(name,age=23,**kv):
"""輸出輸出用戶傳參"""
print("我叫",name,"我的年齡是",age,"dict:",kv)
# 調用含關鍵字參數的函數
default_kv_params("dnt",web="www.baidu.com",qq=110)
來個 綜合案例:def default_god_params(name,age=23,*args,**kv):
需要注意py里面的書寫格式 ==> 先定義再調用(Code參考)
#### 同時定義可變參數+關鍵字參數 ####
def default_god_params(name,age=23,*args,**kv):
"""同時有可變參數+關鍵字參數的使用方法"""
print("我叫",name,"我的年齡是",age,"list:",args,"dict:",kv)
# 調用可變參數+關鍵字參數
# 有名字給kv,沒名字給args
default_god_params("dnt",24,1,2,3,4,5,web="www.baidu.com",qq=110)
#只調用可變參數
default_god_params("dnt",24,1,2,3,4,5)
# 只調用關鍵字參數
default_god_params("dnt",web="www.baidu.com",qq=110)
#### 傳元組和字典 ####
test_tuple=(1,2,3,4,5)
test_dict={"web":"www.baidu.com","qq":110}
default_god_params(*test_tuple,**test_dict)
3.CSharp函數基礎¶
C#
基礎語法大家都很熟了,我貼一個注釋的Code
即可(Code在線)
VSCode
里面如果也想像VS一樣,///
就生成注釋 ==》請安裝函數文檔注釋:XML Documentation Comments
using System;
using System.Collections.Generic;
namespace _6func
{
class Program
{
static void Main(string[] args)
{
// 交換兩數新方式
int x = 3, y = 5;
(x, y) = (y, x);
System.Console.WriteLine("{0},{1}", x, y);
#region Base
// # 定義一個空函數:
Method();
// # 定義一個無參函數
GetName();
// # 定義一個含參函數
ShowInfos("李四", 22);
// # 定義一個含默認參數(缺省參數)的函數
DefaultParam("張三");
// # 定義有返回值的函數
int result = DivHaveReturn(1, 2);
Console.WriteLine($"計算結果為{result}");
#endregion
// # 定義含有多個返回值的函數(利用了元組)
var (sum, dvalue) = DivHaveReturns(1, 2);
Console.WriteLine($"sum:{sum},D-value:{dvalue}");
// 傳多個參數系列:
// 引用傳遞(通過元組、列表實現):擴展有可變類型和不可變類型作為形參的對比
var list = new List<int>() { 1, 2, 3, 4, 5 };
Console.WriteLine(DefaultSomeParams(list));
Console.WriteLine(list.Count);//這就是引用傳遞的證明
// # 定義一個可變參數的函數(參數名字一般都是*args)
Console.WriteLine(DefaultParams(1, 2, 3, 4, 5));
// # 定義含關鍵字參數的函數 直接傳Dict
}
#region base
/// <summary>
/// 定義一個空函數
/// </summary>
private static void Method()
{
}
/// <summary>
/// 定義一個無參函數
/// </summary>
// private static void GetName()
// {
// Console.WriteLine("你好");
// }
//簡寫
private static void GetName() => Console.WriteLine("你好");
/// <summary>
/// 定義一個含參數的函數
/// </summary>
/// <param name="name">名字</param>
/// <param name="age">年齡</param>
// private static void ShowInfos(string name, int age)
// {
// Console.WriteLine($"我叫{name} 我的年齡是{age}");
// }
//簡寫
private static void ShowInfos(string name, int age) => Console.WriteLine($"我叫{name} 我的年齡是{age}");
/// <summary>
/// 定義一個含缺省參數的函數
/// </summary>
/// <param name="name">名字</param>
/// <param name="age">年齡默認23</param>
// private static void DefaultParam(string name, int age = 23)
// {
// Console.WriteLine($"我叫{name} 我的年齡是{age}");
// }
//簡寫
private static void DefaultParam(string name, int age = 23) => Console.WriteLine($"我叫{name} 我的年齡是{age}");
/// <summary>
/// 定義一個有返回值的函數(計算a+b,返回計算結果)
/// </summary>
/// <param name="a">num1</param>
/// <param name="b">num2</param>
// private static int DivHaveReturn(int a, int b)
// {
// return a + b;
// }
//簡寫
private static int DivHaveReturn(int a, int b) => a + b;
#endregion
/// <summary>
/// 定義含有多個返回值的函數(利用了元組)
/// 計算a+b的和,計算a-b,並返回兩個結果
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
// private static (int sum,int dValue) DivHaveReturns(int a, int b)
// {
// return ((a+b),(a-b));
// }
//簡寫
private static (int sum, int dValue) DivHaveReturns(int a, int b) => ((a + b), (a - b));
#region 傳入多個參數系列
/// <summary>
/// 利用列表實現,引用傳遞之類的C#還有ref和out,這邊就不說了
/// </summary>
/// <param name="nums"></param>
/// <returns></returns>
private static int DefaultSomeParams(List<int> numList)
{
int sum = 0;
foreach (var item in numList)
{
sum += item;
}
numList.Clear();
return sum;
}
/// <summary>
/// 定義一個可變參數的函數
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
private static int DefaultParams(params int[] args)
{
int sum = 0;
foreach (var item in args)
{
sum += item;
}
return sum;
}
#endregion
}
}
# 函數遞歸調用其實就是自己調用自己,關鍵點只要考慮什么時候跳出即可(沒有跳出就是死循環)
# 階乘案例 n!
def factorial(n):
if n==1:
return n #跳出
elif n>1:
return n*factorial(n-1) #規律公式
print(factorial(4))
print(factorial(-4))
來個案例(實際工作中並不會自己定義,用系統自帶strip方法即可)
# 利用切片操作,實現一個trim()函數,去除字符串首尾的空格
# 跳出點==> 第一個字符和最后一個字符不是空格
def my_trim(input_str):
if input_str[0] != " " and input_str[-1] != " ":
return input_str
elif input_str[0]==" ":
return my_trim(input_str[1:])#從第二個到最后一個字符
elif input_str[-1]==" ":
return my_trim(input_str[:-1])#從第一個到倒數第二個(end_index取不到)
print(my_trim("我去 "))
print(my_trim(" 我去"))
print(my_trim(" 我去 "))
4.2.CSharp遞歸¶
C#的遞歸我就引用一下以前的(Code)
using System;
namespace _10.遞歸案例
{
class Program
{
//一列數的規則如下: 1、1、2、3、5、8、13、21、34...... 求第30位數是多少, 用遞歸算法實現
static void Main(string[] args)
{
//1 2 3 4 5 n
//1 1 1+1 2+1 3+2 Fn(n-2)+Fn(n-1)
Console.WriteLine(GetNum(30));
Console.ReadKey();
}
/// <summary>
/// 遞歸就這么理解,先找能打破重復的條件,然后就不斷的重復去吧
/// </summary>
/// <param name="n"></param>
/// <returns></returns>
private static int GetNum(int n)
{
if (n == 1 || n == 2)
{
return 1;
}
else
{
return GetNum(n - 1) + GetNum(n - 2);
}
}
}
}
# Python對匿名函數的支持有限,只有一些簡單的情況下可以使用匿名函數
# lambda 參數: 表達式
# 來個簡單求和案例:
sum=lambda a,b: a+b
sum(1,2) #調用一下看看(有點js的感覺,函數可以直接賦值給變量,然后直接用)
來個經常用的排序案例:data_list.sort(key=lambda x:x["key"])
還有一個比較常用的地方 == > 當參數傳遞 def sum(a,b,func):
sum(1,2,lambda x,y: x+y)
先看案例:
# 來個實際案例,還記得list的排序嗎?
# 這次按照指定key排序(有點 SQL 里面 order by 的感覺)
data_list=[
{"name":"a張三","age":21},
{"name":"b李四","age":23},
{"name":"a王五","age":22}]
# 按照age排序
data_list.sort(key=lambda x:x["age"])
print(data_list)
# 按照name排序
data_list.sort(key=lambda x:x["name"])
print(data_list)
# 可以看看幫助文檔
help(data_list.sort)
# 當函數的參數傳遞(有時候需要傳一個匿名方法去函數中處理某些事情)
def sum(a,b,func):
return func(a,b)
sum(1,2,lambda x,y: x+y)
4.4.全局變量和局部變量¶
有人可能會說,這個就太簡單了,沒啥好說的,(⊙o⊙)… Python
還真需要說說,來個案例給你猜結果:
# 有人可能會說,這個就太簡單了,沒啥好說的,(⊙o⊙)… Python還真需要說說
a=100
b=[1,2]
def set_num(num):
num+=num
set_num(a)
set_num(b)
# 發現全局變量a木有被修改,而b修改了
print(a)
print(b)
直接看結果吧:發現全局變量a木有被修改,而b修改了?
啥情況呢?來個簡單案例說明下吧~
# 還是來個簡單案例說明下吧~
a=100
def set_num(num):
a=200
set_num(a)
print(a)
這是因為,python
定義一個變量的寫法不用加類型導致的(⊙﹏⊙)
所以函數里面a=200
,python解釋器就認為 你定義了一個和a這個全局變量名字相同的局部變量
那怎么用呢?global
來聲明一下全局變量即可:
# a=200,python解釋器就認為你定義了一個和a這個全局變量名字相同的局部變量,那怎么用呢?global來聲明一下即可
a=100
def set_num(num):
global a
a=200
set_num(a)
print(a)
總結:
在函數中不使用global
聲明全局變量不能修改全局變量 ==> 本質是不能修改全局變量的指向,即 不能將全局變量指向新的數據
對於 不可變類型的全局變量來說,因其 指向的數據不能修改,所以不使用global
時無法修改全局變量
對於 可變類型的全局變量來說,因其 指向的數據可以修改,所以不使用global
時也可修改全局變量
4.5.可變類型和不可變類型¶
有人可能會問了,之前基礎部分自增自減說了一下:
Python分為:可變類型:list,dict,set等等 和 不可變類型:int,str,tuple,float等等
后來Dict添加修改又提了一下,還沒太清楚怎么辦?
不用慌,今天統統解決,先來個案例看看怎么個可變和不可變
吧:
a="mmd"
a[0]
# 沒法修改
a[0]='d'
a=['m','m','d']
a[0]
a[0]='n'
a
這個直觀吧,先這樣理解 ==> 可變就是可修改,不可變就是不可修改
其實有個小技巧:看可變不可變就看修改前和修改后的id是否相同,不同就是不可變,相同則是可變
在C#或者其他語言里面int是可以修改的,可能對Python的int是不可變類型
有所懷疑,所以我們驗證一下
i=1
id(i)
# 地址改變了
i+=1
id(i)
# 地址又變了
i=i+1
id(i)
可以看到執行 i+=1
時,內存地址都會變化,更加證實了int類型是不可變類型
再來個案例
j=1
id(j) # 有沒有發現,跟一開始i的內存地址一樣了
對於不可變類型int
,無論創建多少個不可變類型,只要值相同,都指向同個內存地址
# 更直觀的來幾個案例
a=1
b=1
c=1
# 不僅僅都一樣,還和所有等於1的變量地址都相同
# 這下不懷疑了吧
print(id(a))
print(id(b))
print(id(c))
不懷疑后,再來個拓展 ~ float是否一樣呢
?
來個案例看看
f1=1.2
id(f1)
# 地址變了,說明float也是不可變類型
f1+=1.0
id(f1)
但是下面部分就和int不一樣咯~
聲明兩個相同值的浮點型變量,查看它們的id,發現它們並不是指向同個內存地址(這點和int類型不同)
這方面涉及Python內存管理機制,Python對int類型和較短的字符串進行了緩存,無論聲明多少個值相同的變量,實際上都指向同個內存地址
總之知道float
也是不可變類型即可
f2=1.2
f3=1.2
print(id(f2))
print(id(f3))
不可變看完了,來看看可變類型:
list1=[12,11,1]
id(list1)
# 改完還是自己原來的地址~說明是可變類型
list1.append(0)
id(list1)
下面驗證一下:當存在多個值相同的不可變類型變量時,看看它們是不是跟可變類型一樣指向同個內存地址:
結論:當存在多個值相同的不可變類型變量時,並不是指向同一個地址
# 當存在多個值相同的不可變類型變量時,並不是指向同一個地址
list2=[12,11,1]
list3=[12,11,1]
print(id(list2))
print(id(list3))
老規矩,來個擴展:
想同一個地址怎么辦?直接賦值即可(注意下面的一種情況)
print(id(list2))
print(id(list3))
list3=list2
print(id(list3)) # 現在list3的地址和list2地址一樣了
# 注意這種情況,如果對其中一個list修改,那么另一個也就跟着被修改了
# 任意一個List進行修改,都會影響另外一個List的值
list2.append(0)
print(list2)
print(list3)
print(id(list2))
print(id(list3))
關於可變類型和不可變類型今天就到這吧,下次還會再說更高深的知識點的,盡請期待哦~
4.6.驗證擴展系列¶
下面就進入驗證擴展系列,看看一些概念:
1.之前很多資料說python3的匿名函數里不能調用print函數,自己測試下:
# 之前很多資料說py3匿名函數里不能調用print函數,自己測試下
# 定義一個無參匿名函數
printf=lambda:print("I Love You")
printf()
2.可變類型與不可變類型的變量分別作為函數參數的區別
感到疑惑可以看之前的運算符擴展(點我)
上面講完可變和不可變再看這個就太輕松了~
Python中函數參數都是引用傳遞
對於不可變類型,因變量不能修改,所以運算不會影響到變量自身
而對於可變類型來說,函數體中的運算有可能會更改傳入的參數變量
# 可變類型與不可變類型的變量分別作為函數參數的區別
def default_some_params(nums):
nums+=nums
test_num=1
default_some_params(test_num)
test_num
test_list=list(range(1,6))
default_some_params(test_list)
test_list
3.函數名能不能重復的問題
在C#或者Java之中,能不能重載:具有不同的參數的類型或參數的個數【跟返回值沒關系】
結論:函數名不能重名
# 函數名能不能重復的問題(能不能重載:具有不同的參數的類型或參數的個數)
def test():
pass
def test(a,b):
return a+b
test(1,2)
test() #前一個直接被后一個覆蓋掉了
4.這次說說兩個有趣的擴展
交換兩數這個之前就說過了,這邊再提一下:
# 交換兩數~元組的方式
a=1
b=2
a,b=b,a # 寫全:(a,b)=(b,a)
print(a)
print(b)
C#的再回顧一下:
int x = 1, y = 2;
(x, y) = (y, x);
Console.WriteLine("x: " + x + " y: " + x);
5.eval
(和js里面的eval差不多):不太建議使用
# 2.eval(和js里面的eval差不多):
input_str=input("請輸入:")
print(input_str)
eval(input_str)