Python3 與 C# 擴展之~基礎衍生

 

本文適應人羣:C# or Python3 基礎鞏固html

代碼褲子: https://github.com/lotapp/BaseCodejava

在線編程: https://mybinder.org/v2/gh/lotapp/BaseCode/masternode

在線預覽:http://github.lesschina.com/python/base/ext/基礎衍生.htmlpython

立刻快期末考試了,老師蜜月也回來了,因而有了一場跨季度的複習講課了:git

1.Python基礎語法擴展

1.1.if 判斷條件相關

None、""、0、[]、{} ==> 假github

一、" "、[None,""]、{"":None} ==> 真算法

小明可高興了,前幾天被打擊的面目全非,這幾天老師回來了,又能夠大發神威了,因而搶先提交demo:編程

In [1]:
# None
if None:
    print(True)
else:
    print(False)
 
False
In [2]:
# 0爲False
if 0:
    print(True)
else:
    print(False)
 
False
In [3]:
# 空字符串
if "":
    print(True)
else:
    print(False)
 
False
In [4]:
# 空列表爲False
if []:
    print(True)
else:
    print(False)
 
False
In [5]:
# 空字典爲False
if {}:
    print(True)
else:
    print(False)
 
False
In [6]:
# 1爲True
if 1:
    print(True)
else:
    print(False)
 
True
In [7]:
# 含空格
if " ":
    print(True)
else:
    print(False)
 
True
In [8]:
if [None,""]:
    print(True)
else:
    print(False)
 
True
In [9]:
if {"":None}:
    print(True)
else:
    print(False)
 
True
 

老師微帶笑容的看了小明一眼,而後接着講if的擴展api

1.2.三元表達符

eg:max = a if a > b else b數組

In [10]:
a, b = 1, 2

max = a if a > b else b

print(max)
 
2
In [11]:
a, b, c = 1, 3, 2

max = a if a > b else b
max = max if max > c else c

print(max)
 
3
In [12]:
# 上面的那個還有一種簡寫(不推薦)
a, b, c = 1, 3, 2

max = (a if a > b else b) if (a if a > b else b) > c else c

print(max)
 
3
 

1.2.字符串和編碼

Python3.x版本中,字符串是以Unicode編碼的

對於單個字符的編碼,Python提供了ord()函數獲取字符的整數表示,chr()函數把編碼轉換爲對應的字符

小潘對這塊有所研究,把小明按在桌上而後搶先提交demo:

In [13]:
ord('D')
Out[13]:
68
In [14]:
ord('毒')
Out[14]:
27602
In [15]:
chr(68)
Out[15]:
'D'
In [16]:
chr(27602)
Out[16]:
'毒'
In [17]:
print(ord('A'))
print(ord('Z'))

print(ord('a'))
print(ord('z'))
 
65
90
97
122
 

老師補充講解道:

編碼:encode() 解碼:decode()

url相關的能夠用:

urllib.parse.quote() and urllib.parse.unquote()

urllib.parse.urlencode() 能夠直接對一個key-value進行url編碼

In [18]:
# encode() and decode()
name="毒逆天"

name_encode=name.encode("utf-8")

print(name_encode)

print(name_encode.decode("utf-8"))
 
b'\xe6\xaf\x92\xe9\x80\x86\xe5\xa4\xa9'
毒逆天
In [19]:
# 須要導入urlib.parse

import urllib.parse
In [20]:
test_str="淡定"

# 對字符串進行url編碼和解碼
test_str_enode = urllib.parse.quote(test_str)

print(test_str_enode)

# urllib.parse.quote() 解碼
print(urllib.parse.unquote(test_str_enode))
 
%E6%B7%A1%E5%AE%9A
淡定
In [21]:
# urlencode 能夠直接對一個key-value進行編碼

test_dict={"name":"毒逆天","age":23}

encode_str = urllib.parse.urlencode(test_dict)

print(encode_str)
print(urllib.parse.unquote(encode_str))
 
name=%E6%AF%92%E9%80%86%E5%A4%A9&age=23
name=毒逆天&age=23
 

1.3.值判斷和地址判斷

小明不樂意了,你個小潘老是搶個人風頭,看完標題就刷刷的在黑板上寫下了以下知識點:

is 是比較兩個引用是否指向了同一個對象id()獲得的地址同樣則相同)

== 是比較兩個對象的值是否相等

在以前講Dict的時候提了一下可變和不可變類型:http://www.javashuo.com/article/p-ftgdkctm-g.html

Func裏面又系統的說了一下:http://www.javashuo.com/article/p-obgfrmjz-g.html

對於可變不可變系列就不去複述了,下面再來幾個案例看看 值判斷地址判斷的概念

In [22]:
################ 可變類型 ################ 
In [23]:
a=[1,2,3]
b=[1,2,3]

# id不同,那is確定不同了
print(id(a))
print(id(b))
 
139727165899464
139727165725256
In [24]:
# a和b是否指向同一個地址
a is b
Out[24]:
False
In [25]:
# a和b的值是否相同
a == b
Out[25]:
True
In [26]:
################ 開始變化了 ################ 
In [27]:
# 讓a指向b的地址
a=b

# a和b的id同樣了
print(id(a))
print(id(b))
 
139727165725256
139727165725256
In [28]:
# a和b是否指向同一個地址
a is b
Out[28]:
True
In [29]:
# a和b的值是否相同
a == b
Out[29]:
True
In [30]:
################ 不可變類型 ################ 
In [31]:
a=1
b=1

# id同樣
print(id(a))
print(id(b))
 
94592578394656
94592578394656
In [32]:
a is b
Out[32]:
True
In [33]:
a == b
Out[33]:
True
In [34]:
# 可是你要注意,不是全部不可變類型都這樣的

f1=1.2
f2=1.2

# 聲明兩個相同值的浮點型變量,查看它們的id,發現它們並非指向同個內存地址(這點和int類型不一樣)
print(id(f1))
print(id(f2))
 
139727217917024
139727217917096
In [35]:
# 這個就不同了
# 這方面涉及Python內存管理機制,Python對int類型和較短的字符串進行了緩存
# 不管聲明多少個值相同的變量,實際上都指向同個內存地址,其餘的就沒這福利咯~

f1 is f2
Out[35]:
False
In [36]:
f1 == f2
Out[36]:
True
 

2.Python總結之for系列

老師徐徐道來:「以前說for老是零零散散的,如今基礎都講完了,來個小彙總:」

2.1.Base

可以被for循環遍歷的,就是可迭代的

For基礎系:http://www.javashuo.com/article/p-dwdjxhac-g.html

In [37]:
# 相似於for(int i=0;i<5;i++)

for i in range(5):
    print(i)
 
0
1
2
3
4
In [38]:
#while循環通常經過數值是否知足來肯定循環的條件
#for循環通常是對能保存多個數據的變量,進行遍歷

name="https://pan.baidu.com/s/1weaF2DGsgDzAcniRzNqfyQ#mmd"

for i in name:
    if i=='#':
        break
    print(i,end='')#另外一種寫法:print("%s"%i,end="")
print('\n end ...')
 
https://pan.baidu.com/s/1weaF2DGsgDzAcniRzNqfyQ
 end ...
In [39]:
# 你指望的結果是:i = 5

for i in range(10):
    if i == 5:
        print("i = %d" % i)
else:
    print("沒有找到")
 
i = 5
沒有找到
In [40]:
# 當迭代的對象迭代完併爲空時,位於else的子句將執行
# 而若是在for循環中含有break時則直接終止循環,並不會執行else子句
# 正確寫法以下:

for i in range(10):
    if i == 5:
        print("i = %d" % i)
        break
else:
    print("沒有找到")
 
i = 5
In [41]:
# 遍歷一個字典

test_dict={"Name":"小明","Age":23}

for k,v in test_dict.items():
    print("key:%s,value:%s"%(k,v))
 
key:Name,value:小明
key:Age,value:23
 

2.2.列表生成式

若是下面知識點還不熟悉的,看看以前講的~列表生成式:http://www.javashuo.com/article/p-ftgdkctm-g.html

簡寫list(range(1, 11)) 全寫[x for x in range(1,11)]

In [42]:
list(range(1, 11))
Out[42]:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
In [43]:
[x for x in range(1,11)]
Out[43]:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
In [44]:
# 1~10的平方列表
[x*x for x in range(1,11)]
Out[44]:
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
In [45]:
# 1~10之間的偶數
[x for x in range(1, 11) if x % 2 == 0]
Out[45]:
[2, 4, 6, 8, 10]
In [46]:
# 數學裏面的全排列
[x + y for x in 'ABC' for y in 'AB']
Out[46]:
['AA', 'AB', 'BA', 'BB', 'CA', 'CB']
In [47]:
# 數學裏面的座標軸
[(x,y) for x in range(1,5) for y in range(1,4)]
Out[47]:
[(1, 1),
 (1, 2),
 (1, 3),
 (2, 1),
 (2, 2),
 (2, 3),
 (3, 1),
 (3, 2),
 (3, 3),
 (4, 1),
 (4, 2),
 (4, 3)]
In [48]:
# (x,y,z) 通常三個嵌套就上天了
[(x,y,z) for x in range(1,5) for y in range(1,4) for z in range(1,3)]
Out[48]:
[(1, 1, 1),
 (1, 1, 2),
 (1, 2, 1),
 (1, 2, 2),
 (1, 3, 1),
 (1, 3, 2),
 (2, 1, 1),
 (2, 1, 2),
 (2, 2, 1),
 (2, 2, 2),
 (2, 3, 1),
 (2, 3, 2),
 (3, 1, 1),
 (3, 1, 2),
 (3, 2, 1),
 (3, 2, 2),
 (3, 3, 1),
 (3, 3, 2),
 (4, 1, 1),
 (4, 1, 2),
 (4, 2, 1),
 (4, 2, 2),
 (4, 3, 1),
 (4, 3, 2)]
 

2.3.擴展

若是要對list實現相似C#或者java那樣的下標循環怎麼辦?

這塊小明又有預習,因而在提交Code的同時大聲說道:

Python內置的enumerate函數能夠把一個list變成索引-元素對,這樣就能夠在for循環中同時迭代索引和元素自己

In [49]:
for i, item in enumerate(['A', 'B', 'C']):
    print(i, item)
 
0 A
1 B
2 C
 

3.Python中賦值、淺拷貝、深拷貝

看到標題小明和小潘就楞了,老師當時沒講解啊,而後兩我的眼巴巴的看着老師講解:

官方文檔:https://docs.python.org/3/library/copy.html

3.1.賦值

經過=來實現,就是把地址拷貝了一份,好比 a = b

In [50]:
a=[1,2,2]
b = a

print(id(a))
print(id(b))
 
139727165518536
139727165518536
In [51]:
# 再驗證

a.append(3)

# 都增長了一個3,說明的確指向同一個內存地址
print(a)
print(b)
 
[1, 2, 2, 3]
[1, 2, 2, 3]
 

3.2.深拷貝deepcopy

導入copy模塊,調用deepcopy方法

若是有嵌套引用的狀況,直接遞歸拷貝

In [52]:
import copy

a=[1,2,2]
In [53]:
b=copy.deepcopy(a)

# 指向了不一樣的內存地址
print(id(a))
print(id(b))
 
139727165899080
139727165900488
In [54]:
# 再驗證一下

a.append(3)

# b不變,說明的確指向不一樣的內存地址
print(a)
print(b)
 
[1, 2, 2, 3]
[1, 2, 2]
In [55]:
################ 開始變化了 ################ 
In [56]:
# 以前講了嵌套列表,咱們來驗證一下

a=[1,2,2]
b=[1,2,3,a]

c=copy.deepcopy(b)

# 發現地址都不同
print(id(b))
print(id(c))
print(id(b[3]))
print(id(c[3]))
 
139727166586248
139727165899080
139727165725256
139727165899464
In [57]:
# 直觀的驗證一下

a.append(666)

# 深拷貝的確是深拷貝
print(b)
print(c)
 
[1, 2, 3, [1, 2, 2, 666]]
[1, 2, 3, [1, 2, 2]]
 

3.3.淺拷貝copy

copy只是簡單拷貝,若是拷貝內容裏面還有引用之類的,他是無論的

In [58]:
import copy

a=[1,2,2]
In [59]:
b=copy.copy(a)

# 指向了不一樣的內存地址
print(id(a))
print(id(b))
 
139727165902088
139727165850952
In [60]:
################ 開始變化了 ################ 
In [61]:
# 以前講了嵌套列表,咱們來驗證一下

a=[1,2,2]
b=[1,2,3,a]

c=copy.copy(b)

# 第一層地址不同
print(id(b))
print(id(c))
 
139727165519432
139727165902088
In [62]:
# 驗證一下
b.append(111)

# 第一層指向的不一樣地址
print(b)
print(c)
 
[1, 2, 3, [1, 2, 2], 111]
[1, 2, 3, [1, 2, 2]]
In [63]:
# 若是裏面還有引用,那麼就無論了
print(id(b[3]))
print(id(c[3]))
 
139727165725576
139727165725576
In [64]:
# 驗證一下
a.append(666)

# 內部引用的確沒copy新地址
print(b)
print(c)
 
[1, 2, 3, [1, 2, 2, 666], 111]
[1, 2, 3, [1, 2, 2, 666]]
 

3.4.知識擴展

若是拷貝的對象是不可變類型,無論深拷貝和淺拷貝以及賦值都是地址引用。但當拷貝的不可變對象含有引用類型時,只有深拷貝(deepcopy)會遞歸複製

須要注意的是:Python和Net對於值類型處理是不同的(管理方式不同致使的)

==>NET中值類型默認是深拷貝的,而對於引用類型,默認實現的是淺拷貝

In [65]:
a=(1,2,2)
b=a

print(id(a))
print(id(b))
 
139727165526520
139727165526520
In [66]:
a=(1,2,2)
b=copy.deepcopy(a)

print(id(a))
print(id(b))
 
139727165846872
139727165846872
In [67]:
a=(1,2,2)
b=copy.copy(a)

print(id(a))
print(id(b))
 
139727165526520
139727165526520
 

擴:當拷貝的不可變對象含有引用類型時:賦值和淺拷貝不會copy,而深拷貝(deepcopy)會遞歸複製 深拷貝.png

PS:咱們經常使用的切片至關於淺拷貝copy.copy()切片.png

4.CSharp中賦值、淺拷貝、深拷貝

小明聽懂了Python的深拷貝和淺拷貝後,本着學以至用的原則,寫下了C#的實現:

先聲明一下,本機環境是Ubuntu + NetCore,歡迎貼Code補充

4.1.賦值

Code:https://github.com/lotapp/BaseCode/tree/master/netcore/3_Ext/deepcopy

賦值方法和Python同樣,直接賦值便可

var list1 = new List<int>() { 1, 2, 2 };
var list2 = list1;
In [68]:
%%script csharp

// Python同樣,直接賦值便可
var list1 = new List<int>() { 1, 2, 2 };
var list2 = list1;

// 驗證一下
list1.Add(3);//咱們修改一下list1,list2也就跟着就改變了

foreach (var item in list1)
{
    Console.Write(item + " ");
}
Console.WriteLine();
foreach (var item in list2)
{
    Console.Write(item + " ");
}
 
1 2 2 3 
1 2 2 3 
 

4.2值類型默認深拷貝

NetCore深拷貝相關的官方文檔 public void CopyTo (T[] array);

簡單類型用最簡單的方式就能實現深拷貝了:

官方的CopyTo在這裏和這個效果同樣,可是比較麻煩,這邊就不貼了(Code裏面貼了)

var list3 = new List<int>() { 1, 2, 2 };
var list4 = new List<int>(list3);

// 驗證一下
list3.Add(3);
foreach (var item in list3)
{
    Console.Write(item + " ");
}
Console.WriteLine();
foreach (var item in list4)
{
    Console.Write(item + " ");
}

結果:

1 2 2 3 
1 2 2

4.3.引用類型默認淺拷貝

對於List<T>再複雜點的,上面的方式就變成淺拷貝了:(相似於Python的Copy.Copy)

官方的CopyTo在這裏和這個效果同樣,可是比較麻煩,這邊就不貼了(Demo裏面貼了)

定義一個Student

public partial class Student
{
    public string Name { get; set; }
    public int Age { get; set; }

    public override string ToString()
    {
        return $"Name:{Name},Age:{Age}";
    }
}

淺拷貝Demo:

var list5 = new List<Student>(){
    new Student { Name = "小張", Age = 22 },
    new Student { Name = "小明", Age = 23 }
    };
var p = new Student() { Name = "小潘", Age = 23 };
list5.Add(p);

// 淺拷貝一份
var list6 = new List<Student>(list5);

// 淺拷貝測試
// 咱們修改一下list5,list6沒有跟着改變,說明第一層的地址的確不同
list5.Add(new Student() { Name = "小胖", Age = 24 });
// 當咱們修改小潘同窗的年齡時,你們都變了,說明真的只是淺拷貝
p.Age = 24;

foreach (var item in list5)
{
    Console.WriteLine(item);
}
Console.WriteLine("=============");
foreach (var item in list6)
{
    Console.WriteLine(item);
}

結果:

Name:小張,Age:22
Name:小明,Age:23
Name:小潘,Age:24
Name:小胖,Age:24
=============
Name:小張,Age:22
Name:小明,Age:23
Name:小潘,Age:24
 

4.4.簡單方式實現深拷貝

對於List<T>的深拷貝場景,其實項目中仍是蠻常見的,那深拷貝怎麼搞呢?

先來一個簡單的實現方式,須要T實現ICloneable接口才行:

定義一個Person類

public partial class Person : ICloneable
{
    public string Name { get; set; }
    public int Age { get; set; }

    //實現ICloneable的Clone方法
    public object Clone()
    {
        return base.MemberwiseClone();//調用父類方法便可
    }

    public override string ToString()
    {
        return $"Name:{Name},Age:{Age}";
    }
}

List<T>定義一個擴展方法:(舒適提醒:擴展方法所在的類必須是static Class哦)

public static partial class ListExt
{
    // 只要T實現了ICloneable接口就能夠了
    public static IEnumerable<T> DeepCopy<T>(this IEnumerable<T> list) where T : ICloneable
    {
        return list.Select(item => (T)item.Clone()).ToList();
    }
}

來個調用加驗證:

#region 引用類型深拷貝-簡單實現方式

var oldList = new List<Person>(){
    new Person(){Name="小明",Age=23},
    new Person(){Name="小張",Age=22},
};
var xiaoPan = new Person() { Name = "小潘", Age = 23 };
oldList.Add(xiaoPan);

var newList = oldList.DeepCopy();

//測試
oldList.Add(new Person() { Name = "小胖", Age = 23 });
xiaoPan.Age = 24;

foreach (var item in oldList)
{
    Console.WriteLine(item);
}
Console.WriteLine("========");
foreach (var item in newList)
{
    Console.WriteLine(item);
}

#endregion

結果:

Name:小明,Age:23
Name:小張,Age:22
Name:小潘,Age:24
Name:小胖,Age:23
========
Name:小明,Age:23
Name:小張,Age:22
Name:小潘,Age:23
 

4.5.序列化方式實現深拷貝(經常使用)

利用System.Runtime.Serialization序列化與反序列化實現深拷貝

先定義一個Teacher類(別忘記加 Serializable 的標籤)

[Serializable]
public partial class Teacher
{
    public string Name { get; set; }
    public int Age { get; set; }

    public override string ToString()
    {
        return $"Name:{Name},Age:{Age}";
    }
}

添加一個擴展方法:

public static partial class ListExt
{
    // 利用System.Runtime.Serialization序列化與反序列化實現深拷貝
    public static T DeepCopy2<T>(this T obj)
    {
        using (var stream = new MemoryStream())
        {
            var formatter = new BinaryFormatter();
            formatter.Serialize(stream, obj);
            stream.Seek(0, SeekOrigin.Begin);
            return (T)formatter.Deserialize(stream);
        }
    }
}

調用:

#region 引用類型深拷貝-序列化實現

var oldTestList = new List<Teacher>(){
    new Teacher(){Name="小明",Age=23},
    new Teacher(){Name="小張",Age=22},
};
var s = new Teacher() { Name = "小潘", Age = 23 };
oldTestList.Add(s);

var newTestList = oldTestList.DeepCopy2();

//測試
oldTestList.Add(new Teacher() { Name = "小胖", Age = 23 });
s.Age = 24;

foreach (var item in oldTestList)
{
    Console.WriteLine(item);
}
Console.WriteLine("========");
foreach (var item in newTestList)
{
    Console.WriteLine(item);
}

#endregion

結果:

Name:小明,Age:23
Name:小張,Age:22
Name:小潘,Age:24
Name:小胖,Age:23
========
Name:小明,Age:23
Name:小張,Age:22
Name:小潘,Age:23

由於主要是說Python,Net只是簡單提一下,這邊就先到這裏了

不盡興能夠看看這篇文章,講得仍是挺全面的

咱們接着來對比學習~

 

5.Python生成器

一看到標題小明又懵圈了,可是看到你們好像都知道的樣子心想道:「我是否是又睡過一節課啊?」

以前有講列表生成式,這邊說說生成器

經過列表生成式,咱們能夠簡單並直接的建立一個列表,可是當數據有必定的規律並且又很大的時候,使用列表就有點浪費資源了

若是列表元素能夠按照某種算法推算出來,這樣就沒必要建立完整的list,從而節省大量的資源

5.1.簡單方式

在Python中,這種一邊循環一邊計算的機制,稱爲生成器:generator

先看一個簡單的生成器案例:(只要把一個列表生成式的[]改爲() ,就建立了一個generator了)

In [69]:
# 列表生成式
[x for x in range(10)]
Out[69]:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [70]:
# 生成器寫法(Python2.x系列是用xrange)
(x for x in range(10))
Out[70]:
<generator object <genexpr> at 0x7f14c413cb48>
 

遍歷方式能夠用以前的for循環來遍歷(推薦)

也能夠用next()或者__next__()方法來遍歷。【C#是用MoveNext

generator保存的是算法,每次調用next(xxx)或者__next__(),就計算出下一個元素的值,直到計算到最後一個元素

當沒有更多的元素時,拋出StopIteration的異常

最新的Python3.7在這方面有所優化:https://www.python.org/dev/peps/pep-0479

In [71]:
g=(x for x in range(10))

# for來遍歷(推薦)
for i in g:
    print(i)
 
0
1
2
3
4
5
6
7
8
9
In [72]:
g=(x for x in range(10))

print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(g.__next__()) #經過__next__也同樣取下一個
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
 
0
1
2
3
4
5
6
7
8
9
 
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-72-9897a9148994> in <module>()
     11 print(next(g))
     12 print(next(g))
---> 13print(next(g))
     14 print(next(g))

StopIteration: 
 

5.2.yield方式

若是推算的算法比較複雜,用相似列表生成式的for循環沒法實現時,還能夠用函數來實現

這時候就須要用到yield了,像最經典的斐波拉契數列,此次用一波生成器來對比實現下:

In [73]:
# 遞歸方式:求第30個數是多少

# 一、一、二、三、五、八、1三、2一、34...
def fib(n):
    if n == 1 or n == 2:
        return 1
    else:
        return fib(n - 1) + fib(n - 2)

fib(30)
Out[73]:
832040
In [74]:
# 在講yield方式以前先用循環實現一下

def fibona(max):
    n, a, b = 0, 0, 1
    while n < max:
        print(b)
        a, b = b, a + b
        n = n + 1

fibona(30)
 
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946
17711
28657
46368
75025
121393
196418
317811
514229
832040
In [75]:
# for循環實現

def fibona(n):
    a, b = 0, 1
    # [0,n)
    for i in range(n):
        print(b)
        a, b = b, a + b

fibona(30)
 
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946
17711
28657
46368
75025
121393
196418
317811
514229
832040
 

a, b = b, a + b 以前交換兩數的時候提過

這個至關於==>

temp_tuple = (b, a + b)
a = temp_tuple[0]
b = temp_tuple[1]

要把fibona函數變成generator,只須要把print(b)改成yield b就能夠了:

generator在執行過程當中,遇到yield就中斷,下次又繼續執行到yield停下了,一直到最後

生成器的特色:

  1. 節約內存
  2. 迭代到下一次的調用時,所使用的參數都是第一次所保留下的(全部函數調用的參數都是第一次所調用時保留的,而不是新建立的)
In [76]:
# 改爲生成器比較簡單,直接換輸出爲yield

def fibona(n):
    a, b = 0, 1
    # [0,n)
    for i in range(n):
        yield b
        a, b = b, a + b
In [77]:
# 看看是否是生成器
g = fibona(30)

g
Out[77]:
<generator object fibona at 0x7f14c40efd58>
In [78]:
# 遍歷輸出(基本上都會用for來遍歷)
for i in g:
    print(i)
 
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946
17711
28657
46368
75025
121393
196418
317811
514229
832040
 

對於函數改爲的generator來講,遇到return語句或者執行到函數體最後一行語句,就是結束generator的循環的時候

小明總結以下:

  1. 在Python中,這種一邊循環一邊計算的機制稱爲生成器:generator

  2. 每個生成器都是一個迭代器(迭代器不必定是生成器)

  3. 若是一個函數包含yield關鍵字,這個函數就會變爲一個生成器

  4. 生成器並不會一次返回全部結果,而是每次遇到yield關鍵字後返回相應結果,並保留函數當前的運行狀態,等待下一次的調用

  5. 因爲生成器也是一個迭代器,那麼它就支持next用方法來獲取下一個值(咱們平時用for來遍歷它)

推薦一篇文章,總結的很全了:(yield用法總結

5.3.擴展之~send(msg)方法:

其實__next__()send()在必定意義上做用是類似的,區別是send()能夠傳遞yield表達式的值進去

__next__()不 能傳遞特定的值。咱們能夠看作x.__next__()x.send(None) 做用是同樣的

In [79]:
# 來個案例:
def test_send(n):
    for i in range(n):
        tmp = yield i
        print(tmp)


g = test_send(5)

g
Out[79]:
<generator object test_send at 0x7f14c40efdb0>
In [80]:
# 定義一個列表
test_list = []

# 把第一次yield的值放在列表中
test_list.append(g.__next__())

# 把list傳給tmp並打印(能夠理解爲把表達式右邊的 yield i 暫時換成了 test_list)
# out的內容是yield返回的值
g.send(test_list)
 
[0]
Out[80]:
1
In [81]:
# 以防大家看不懂,來個簡單案例
# 你傳啥print(tmp)就給你打印啥
g.send("你好啊")
 
你好啊
Out[81]:
2
 

注意一種狀況,generator剛啓動的時候,要麼不傳,要麼只能傳None

解決:要麼一開始send(None)要麼一開始先調用一下__next()__ or next()

In [82]:
# 注意一種狀況,generator剛啓動的時候,要麼不傳,要麼只能傳None
def test_send(n):
    for i in range(n):
        tmp = yield i
        print(tmp)


g = test_send(5)
g.send("dog") # TypeError: can't send non-None value to a just-started generator
 
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-82-2e891aa5dd81> in <module>()
      7 
      8 g = test_send(5)
----> 9g.send("dog") # TypeError: can't send non-None value to a just-started generator

TypeError: can't send non-None value to a just-started generator
In [83]:
# 解決:要麼一開始send(None)要麼一開始先調用一下__next()__ or next()
def test_send(n):
    for i in range(n):
        tmp = yield i
        print(tmp)


g = test_send(5)
g.send(None)
Out[83]:
0
In [84]:
g.send("dog")
 
dog
Out[84]:
1
 

擴:C#在遍歷generator的時候也是先調一下MoveNext方法

while (tmp.MoveNext())
{
    Console.WriteLine(tmp.Current);
}
 

5.4.擴展之~returnbreak的說明

在一個generator函數中,若是沒有return則默認執行至函數完畢

若是在執行過程當中return或者break則直接拋出StopIteration終止迭代

In [85]:
# break案例
def test_send(n):
    for i in range(n):
        if i==2:
            break
        yield i

g = test_send(5)
for i in g:
    print(i)
 
0
1
In [86]:
# return案例
def test_send(n):
    for i in range(n):
        if i==2:
            return "i==2"
        yield i

g = test_send(5)
for i in g:
    print(i)
 
0
1
 

for循環調用generator時,發現拿不到generatorreturn語句的返回值

若是想要拿到返回值,必須捕獲StopIteration錯誤,返回值包含在StopIteration的value

In [87]:
# 上面return的返回值怎麼拿呢?

g = test_send(5)

while True:
    try:
        tmp = g.__next__()
        print(tmp)
    except StopIteration as ex:
        print(ex.value)
        break # 必定要加break,別忘了你在死循環裏呢
 
0
1
i==2
 

5.5.擴展之~協程yield實現多任務調度

這個場景仍是很常見的,好比C#的單線程實現多任務用的就可使用yield

再好比生產消費這個經典案例:(參考

生產者生產消息後,直接經過yield跳轉到消費者開始執行,待消費者執行完畢後,切換回生產者繼續生產

Python對協程的支持是經過generator實現的

在generator中,咱們不但能夠經過for循環來迭代,還能夠不斷調用__next__()獲取由yield語句返回的下一個值。

由於Python的yield不但能夠返回一個值,它還能夠接收調用者發出的參數(經過send方法),因此就happy了

咱們舉個簡單的demo來看看:

In [88]:
def consumer():
    while True:
        tmp = yield
        # !None就變成真了
        if not tmp:
            return
        print("消費者:",tmp)
In [89]:
# 建立消費者
c = consumer()
# 啓動消費者
c.send(None)
# 生產數據,並提交給消費者
c.send("小明")
c.send("小潘")
# 生產結束,通知消費者結束,拋出StopIteration異常
c.send(None) # 使用c.close()能夠避免異常
 
消費者: 小明
消費者: 小潘
 
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-89-bcc0083d4089> in <module>()
      7 c.send("小潘")
      8 # 生產結束,通知消費者結束,拋出StopIteration異常
----> 9c.send(None) # 使用c.close()能夠避免異常

StopIteration: 
 

執行流程

  1. 建立協程對象(消費者)後,必須使用send(None)__next__()啓動
  2. 協程在執行yield後讓出執行緒,等待消息
  3. 調用方發送send(msg)消息,協程恢復執行,將接收到的數據保存並執行後續流程
  4. 再次循環到yield,協程返回前面的處理結果,並再次讓出執行緒
  5. 直到關閉或被引起異常

補全demo:

In [90]:
def consumer():
    status = ""
    while True:
        tmp = yield status
        if not tmp:
            print("消費者已經睡覺了...")
            return
        print("消費者:得到商品%s號..." % tmp)
        status = "ok"


def produce(c):
    # 啓動消費者
    c.send(None)
    for i in range(1, 3):
        print("生產者:出產商品%s號..." % i)
        # 生產商品,並提交給消費者
        status = c.send(i)
        print("生產者:生產者消費狀態: %s" % status)
    # c.send(None) 執行這個會引起StopIteration
    c.close()  # 使用close就能夠避免了(手動關閉生成器函數,後面的調用會直接返回StopIteration異常)


# 建立消費者
c = consumer()
produce(c)
 
生產者:出產商品1號...
消費者:得到商品1號...
生產者:生產者消費狀態: ok
生產者:出產商品2號...
消費者:得到商品2號...
生產者:生產者消費狀態: ok
In [91]:
# 更多能夠查看幫助文檔
def test():
    yield
help(test())
 
Help on generator object:

test = class generator(object)
 |  Methods defined here:
 |  
 |  __del__(...)
 |  
 |  __getattribute__(self, name, /)
 |      Return getattr(self, name).
 |  
 |  __iter__(self, /)
 |      Implement iter(self).
 |  
 |  __next__(self, /)
 |      Implement next(self).
 |  
 |  __repr__(self, /)
 |      Return repr(self).
 |  
 |  close(...)
 |      close() -> raise GeneratorExit inside generator.
 |  
 |  send(...)
 |      send(arg) -> send 'arg' into generator,
 |      return next yielded value or raise StopIteration.
 |  
 |  throw(...)
 |      throw(typ[,val[,tb]]) -> raise exception in generator,
 |      return next yielded value or raise StopIteration.
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |  
 |  gi_code
 |  
 |  gi_frame
 |  
 |  gi_running
 |  
 |  gi_yieldfrom
 |      object being iterated by yield from, or None

 

6.Python迭代器

看到迭代器小明老高興了,心想着一會寫個C#版的以爲能夠收穫一大羣眼球~

6.1.判斷是否可迭代

在說迭代器前先說下可迭代(Iterable)yield基礎點我):

在Python中,能經過for循環遍歷的都是能夠迭代的,好比 str、tuple、list、dict、set、生成器等等

也能夠經過 isinstance(xxx,Iterable) 方法判斷一下是否迭代:

In [92]:
from collections import Iterable
In [93]:
isinstance("mmd",Iterable)
Out[93]:
True
In [94]:
isinstance((1,2),Iterable)
Out[94]:
True
In [95]:
isinstance([],Iterable)
Out[95]:
True
In [96]:
isinstance({},Iterable)
Out[96]:
True
In [97]:
isinstance((x for x in range(10)),Iterable)
Out[97]:
True
In [98]:
isinstance(1,Iterable)
Out[98]:
False
 

6.2.判斷是不是迭代器

迭代器是必定能夠迭代的,怎麼判斷是迭代器呢?

可使用next方法的或者經過isinstance(xxx,Iterator)

In [99]:
a=[1,2,3]

next(a)
 
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-99-f5f8ac9a8550> in <module>()
      1 a=[1,2,3]
      2 
----> 3next(a)

TypeError: 'list' object is not an iterator
In [100]:
from collections import Iterator
In [101]:
isinstance([],Iterator)
Out[101]:
False
In [102]:
isinstance((x for x in range(10)),Iterator)
Out[102]:
True
 

6.3.IterableIterator

生成器都是Iterator對象,但list、dict、str雖然是Iterable,卻不是Iterator

list、dict、strIterable變成Iterator可使用iter()函數:

In [103]:
iter(a)
Out[103]:
<list_iterator at 0x7f14c40a3da0>
In [104]:
isinstance(iter([]),Iterator)
Out[104]:
True
In [105]:
isinstance(iter({}),Iterator)
Out[105]:
True
 

Python的Iterator對象表示的是一個數據流,Iterator對象能夠被next()or__next__()函數調用並不斷返回下一個數據,直到沒有數據時拋出StopIteration錯誤

能夠把這個數據流看作是一個有序序列,但咱們卻不能提早知道序列的長度,只能不斷經過next函數實現按需計算下一個數據,因此Iterator的計算是惰性的,只有在須要返回下一個數據時它纔會計算。

Iterator甚至能夠表示一個無限大的數據流,而list等則不行

小明總結了一下老師講解的知識點:

  1. 能夠for循環的對象都是Iterable類型

  2. 可使用next()or__next__()函數的對象都是Iterator類型

  3. 集合數據類型如list、dict、str等是Iterable,能夠經過iter()函數得到一個Iterator對象

 

7.CSharp迭代器

乘着下課的時間,小明跑到黑板前,心想:「又到了C#的時候了,看我來收播一大羣眼球~」,而後開始了他的我的秀:

其實迭代器(iterator就是爲了更簡單的建立枚舉器(enumerator)和可枚舉類型(enumerator type)的方式

7.1.IEnumeratorIEnumerable

通俗話講:

能不能foreach就看你遍歷對象有沒有實現IEnumerable,就說明你是否是一個可枚舉類型enumerator type

public interface IEnumerable
{
    IEnumerator GetEnumerator();
}

是否是個枚舉器(enumerator)就看你實現了IEnumerator接口沒

public interface IEnumerator
{
    object Current { get; }

    bool MoveNext();

    void Reset();
}

最明顯的區別:它們兩個遍歷方式不同

// 枚舉器遍歷
var tmp = FibonaByIEnumerator(30);
while (tmp.MoveNext())
{
    Console.WriteLine(tmp.Current);
}
// 可枚舉類型遍歷
foreach (var item in FibonaByIEnumerable(30))
{
    Console.WriteLine(item);
}

這個咱們在2年前就說過,這邊簡單提一下(官方文檔)(Demo)

MyEnumerator文件:

public class MyEnumerator : IEnumerator
{
    /// <summary>
    /// 須要遍歷的數組
    /// </summary>
    private string[] array;
    /// <summary>
    /// 有效數的個數
    /// </summary>
    private int count;
    public MyEnumerator(string[] array, int count)
    {
        this.array = array;
        this.count = count;
    }

    /// <summary>
    /// 當前索引(線moveNext再獲取index,用-1更妥)
    /// </summary>
    private int index = -1;
    public object Current
    {
        get
        {
            return array[index];
        }
    }
    /// <summary>
    /// 移位
    /// </summary>
    /// <returns></returns>
    public bool MoveNext()
    {
        if (++index < count)
        {
            return true;
        }
        return false;
    }
    /// <summary>
    /// 重置
    /// </summary>
    public void Reset()
    {
        index = -1;
    }
}

MyArray.cs文件

public partial class MyArray
{
    /// <summary>
    /// 數組容量
    /// </summary>
    private string[] array = new string[4];
    /// <summary>
    /// 數組元素個數
    /// </summary>
    private int count = 0;
    /// <summary>
    /// 當前數組的長度
    /// </summary>
    public int Length
    {
        get
        {
            return count;
        }
    }

    /// <summary>
    /// 添加元素
    /// </summary>
    /// <param name="str"></param>
    /// <returns></returns>
    public MyArray Add(string str)
    {
        //要溢出的時候擴容
        if (count == array.Length)
        {
            string[] newArray = new string[2 * array.Length];
            array.CopyTo(newArray, 0);
            array = newArray;//array從新指向
        }
        array[count++] = str;
        return this;
    }

    /// <summary>
    /// 移除某一項
    /// </summary>
    /// <param name="i"></param>
    /// <returns></returns>
    public MyArray RemoveAt(int i)
    {
        for (int j = i; j < count - 1; j++)
        {
            array[j] = array[j + 1];
        }
        count--;//少了一個元素因此--
        return this;
    }

    /// <summary>
    /// 索引器
    /// </summary>
    /// <param name="index"></param>
    /// <returns></returns>
    public string this[int index]
    {
        get
        {
            return array[index];
        }
        set
        {
            array[index] = value;
        }
    }
}

MyArrayExt.cs文件:

public partial class MyArray: IEnumerable
{
    /// <summary>
    /// 枚舉器方法
    /// </summary>
    /// <returns></returns>
    public IEnumerator GetEnumerator()
    {
        return new MyEnumerator(this.array, this.count);
    }
}

調用:

static void Main(string[] args)
{
    MyArray array = new MyArray();
    array.Add("~").Add("這").Add("是").Add("一").Add("個").Add("測").Add("試").Add("。").RemoveAt(0).RemoveAt(3).RemoveAt(6);
    for (int i = 0; i < array.Length; i++)
    {
        Console.Write(array[i]);
    }
    Console.WriteLine();
    foreach (var item in array)
    {
        Console.Write(item);
    }
}

結果:

這是一測試
這是一測試
 

7.2.yield方式

小明看着班裏女生羨慕的眼神,得意的強調道:

注意一下,C#是用yield return xxx,Python是用yield xxx關鍵字

還記得開頭說的那句話嗎?(yield官方文檔

其實迭代器(iterator)就是爲了更簡單的建立枚舉器(enumerator)和可枚舉類型(enumerator type)的方式

若是枚舉器和可枚舉類型仍是不理解(舉個例子)就懂了:(從遍歷方式就看出區別了)

定義一個斐波拉契函數,返回可枚舉類型

/// <summary>
/// 返回一個可枚舉類型
/// </summary>
public static IEnumerable<int> FibonaByIEnumerable(int n)
{
    int a = 0;
    int b = 1;
    for (int i = 0; i < n; i++)
    {
        yield return b;
        (a, b) = (b, a + b);
    }
}

調用:

foreach (var item in FibonaByIEnumerable(30))
{
    Console.WriteLine(item);
}

定義一個斐波拉契函數,返回一個枚舉器

/// <summary>
/// 返回一個枚舉器
/// </summary>
public static IEnumerator<int> FibonaByIEnumerator(int n)
{
    int a = 0;
    int b = 1;
    for (int i = 0; i < n; i++)
    {
        yield return b;
        (a, b) = (b, a + b);
    }
}

調用一下:

var tmp = FibonaByIEnumerator(30);
while (tmp.MoveNext())
{
    Console.WriteLine(tmp.Current);
}

利用yield輕輕鬆鬆就建立了枚舉器和可枚舉類型

以上面那個MyArray的案例來講,有了yield咱們代碼量大大簡化:(Demo

MyArray.cs

public partial class MyArray
{
    /// <summary>
    /// 數組容量
    /// </summary>
    private string[] array = new string[4];
    /// <summary>
    /// 數組元素個數
    /// </summary>
    private int count = 0;
    /// <summary>
    /// 當前數組的長度
    /// </summary>
    public int Length
    {
        get
        {
            return count;
        }
    }

    /// <summary>
    /// 添加元素
    /// </summary>
    /// <param name="str"></param>
    /// <returns></returns>
    public MyArray Add(string str)
    {
        //要溢出的時候擴容
        if (count == array.Length)
        {
            string[] newArray = new string[2 * array.Length];
            array.CopyTo(newArray, 0);
            array = newArray;//array從新指向
        }
        array[count++] = str;
        return this;
    }

    /// <summary>
    /// 移除某一項
    /// </summary>
    /// <param name="i"></param>
    /// <returns></returns>
    public MyArray RemoveAt(int i)
    {
        for (int j = i; j < count - 1; j++)
        {
            array[j] = array[j + 1];
        }
        array[count - 1] = string.Empty;//add 幹掉移除的數組
        count--;//少了一個元素因此--
        return this;
    }

    /// <summary>
    /// 索引器
    /// </summary>
    /// <param name="index"></param>
    /// <returns></returns>
    public string this[int index]
    {
        get
        {
            return array[index];
        }
        set
        {
            array[index] = value;
        }
    }
}

MyArrayExt.cs

public partial class MyArray : IEnumerable
{
    /// <summary>
    /// 枚舉器方法
    /// </summary>
    /// <returns></returns>
    public IEnumerator GetEnumerator()
    {
        return MyEnumerator();
    }
    /// <summary>
    /// 經過yield快速實現
    /// </summary>
    /// <returns></returns>
    public IEnumerator<string> MyEnumerator()
    {
        foreach (var item in this.array)
        {
            yield return item;
        }
    }
}

而後就好了,MyEnumerator都不用你實現了:

MyArray array = new MyArray();
array.Add("~").Add("這").Add("是").Add("一").Add("個").Add("測").Add("試").Add("。").RemoveAt(0).RemoveAt(3).RemoveAt(6);
for (int i = 0; i < array.Length; i++)
{
    Console.Write(array[i]);
}
Console.WriteLine();
foreach (var item in array)
{
    Console.Write(item);
}

結果:

這是一測試
這是一測試

擴充一下:Python退出迭代器用yield return 或者 yield breakC#使用yield break來退出迭代

作個 demo 測試下:

public static IEnumerable<int> GetValue()
{
    for (int i = 0; i < 5; i++)
    {
        yield return i;
        if (i == 2)
        {
            yield break;
        }
    }
}

調用:

static void Main(string[] args)
{
    foreach (var item in GetValue())
    {
        Console.WriteLine(item);
    }
}

輸出:

0
1
2
 

8.閉包

8.1.Python閉包

又到了上課時間,小明灰溜溜的跑回座位,聽老師講起了閉包的知識:

函數方面還有不懂的能夠看以前講的文檔:Function Base

函數除了能夠接受函數做爲參數外,還能夠把函數做爲結果值返回(有點相似於C++裏面的函數指針了)

來看一個可變參數求和的例子:

In [1]:
def slow_sum(*args):
    def get_sum():
        sum = 0
        for i in args:
            sum += i
        return sum

    return get_sum  # 返回函數引用地址(不加括號)

a = slow_sum(1, 2, 3, 4, 5)# 返回get_sum函數的引用
print(a)# 看看引用地址
print(a())# a() 這時候纔是調用get_sum()函數
 
<function slow_sum.<locals>.get_sum at 0x7f57783b6268>
15
 

其實上面一個案例就是閉包(Closure)了,來個定義:

在函數內部再定義一個函數,而且這個函數用到了外邊函數的變量(參數或者局部變量),那麼將這個函數以及用到的一些變量稱之爲閉包

通俗點說就是:內部函數使用了外部函數做用域裏的變量了,那這個內部函數和它用到的變量就是個閉包

注意:當咱們調用slow_sum()時,每次調用都會返回一個新的函數(相同的參數也同樣)

In [2]:
a = slow_sum(1, 2, 3, 4)
b = slow_sum(1, 2, 3, 4)

a is b

# a()和b()的調用結果互不影響
Out[2]:
False
 

因爲閉包引用了外部函數的局部變量,則外部函數的局部變量沒有及時釋放,因此也容易消耗內存

so ==> 除非你真正須要它,不然不要使用閉包

返回函數儘可能不要引用任何循環變量,或者後續會發生變化的變量(容易出錯)

看着小明一臉懵圈的樣子,老師說道:

新講的知識點通常都不太容易快速消化,咱們再來看個閉包的好處就理解了:

好比如今咱們要根據公式來求解,以y=ax+b爲例,傳統方法解決:

In [3]:
# 定義一個y=ax+b的函數公式
def get_value(a, b, x):
    return a * x + b
In [4]:
# 每次調用都得傳 a,b
print(get_value(2, 1, 1))
print(get_value(2, 1, 2))
print(get_value(2, 1, 3))
print(get_value(2, 1, 4))
 
3
5
7
9
 

每次調用都得額外傳a、b的值

就算使用偏函數來簡化也不合適(畢竟已是一個新的函數了):

In [5]:
from functools import partial

new_get_value = partial(get_value, 2, 1)

print(new_get_value(1))
print(new_get_value(2))
print(new_get_value(3))
print(new_get_value(4))
print(new_get_value(5))
 
3
5
7
9
11
 

簡單總結functools.partial的做用就是:

把一個函數的某些參數設置默認值,返回一個新的函數,而後調用新函數就省得你再輸入重複參數了

而這時候使用閉包就比較合適了,並且真的是封裝了一個通用公式了

a,b的值你能夠任意變來生成新的公式,並且公式之間還不干擾,以 y=ax²+bx+c爲例:

In [6]:
def quadratic_func(a, b, c):
    """y=ax²+bx+c"""

    def get_value(x):
        return a * x * x + b * x + c

    return get_value
In [7]:
# 來個簡單的:x^2+1
f1 = quadratic_func(1, 0, 1)

print(f1(0))
print(f1(1))
print(f1(2))
print(f1(3))
print(f1(4))
print(f1(5))
 
1
2
5
10
17
26
In [8]:
# 可能不太形象,咱們畫個圖看看:

import matplotlib.pyplot as plt # 導入matplotlib的pyplot模塊
In [9]:
# 生成x和y的值
x_list = list(range(-10, 11))
y_list = [x * x + 1 for x in x_list]

print(x_list)
print(y_list)

# 畫圖
plt.plot(x_list, y_list)
# 顯示圖片
plt.show()
 
[-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[101, 82, 65, 50, 37, 26, 17, 10, 5, 2, 1, 2, 5, 10, 17, 26, 37, 50, 65, 82, 101]
 
In [10]:
# 再來個簡單的:x^2-1
f2 = quadratic_func(1, 0, -1) # 相互之間不干擾

print(f2(0))
print(f2(1))
print(f2(2))
print(f2(3))
print(f2(4))
print(f2(5))
 
-1
0
3
8
15
24
 

8.2.CSharp閉包

聽完閉包老師就下課了,說什麼明天接着閉包講啥裝飾器的。

小明一愣一愣的,而後就屁顛的跑黑板前講起了C#版本的閉包:

先看看怎麼定義一個閉包,和Python同樣,用個求和函數舉例:(返回一個匿名函數

// 有返回值就用Func,沒有就用Action
public static Func<int> SlowSum(params int[] args)
{
    return () =>
    {
        int sum = 0;
        foreach (var item in args)
        {
            sum += item;
        }
        return sum;
    };
}

調用:

static void Main(string[] args)
{
    var f1 = SlowSum(1, 2, 3, 4, 5);
    Console.WriteLine(f1);
    Console.WriteLine(f1());
}

結果:(從結果能夠看到,f1是一個函數,等你調用f1()纔會求和)

System.Func`1[System.Int32]
15

接着講 ~ 以上面的 y=ax²+bx+c爲例,C#實現:

// 以上面的 y=ax²+bx+c 爲例,C#實現:
public static Func<double, double> QuadraticFunc(double a, double b, double c)
{
    return x => a * x * x + b * x + c; // 返回一個匿名函數
}

調用:

static void Main(string[] args)
{
    var func = QuadraticFunc(1, 0, 1);

    Console.WriteLine(func(0));
    Console.WriteLine(func(1));
    Console.WriteLine(func(2));
    Console.WriteLine(func(3));
    Console.WriteLine(func(4));
    Console.WriteLine(func(5));
}

結果:

1
2
5
10
17
26

Func<double,double>不理解就看看定義就懂了:public delegate TResult Func<in T, out TResult>(T arg);

這部分不是很難,簡單提一下知識點便可。若是你想深究能夠==> ( )

在收穫滿滿一籮筐眼球后,小明拍拍屁股去了新開的飯店大吃一頓了...


寫在最後:還有一些內容沒寫,估計過幾天又有一篇叫 「基礎拓展」 的文章了,爲啥不一塊兒寫完呢?

其實逆天也想寫完,真寫完文章又被叫作長篇大論一百頁了 #^_^# 行了,聽取你們意見,不寫那麼長的文章,下次見~

相關文章
相關標籤/搜索