本文适应人群:C# or Python3 基础巩固

代码裤子: https://github.com/lotapp/BaseCode

在线编程: https://mybinder.org/v2/gh/lotapp/BaseCode/master

在线预览:http://github.lesschina.com/python/base/ext/基础衍生.html

马上快期末考试了,老师蜜月也回来了,于是有了一场跨季度的复习讲课了:

1.Python基础语法扩展

1.1.if 判断条件相关

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

1、" "、[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的扩展

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的时候提了一下可变和不可变类型:https://www.cnblogs.com/dotnetcrazy/p/9155310.html#5.2.增加和修改

Func里面又系统的说了一下:https://www.cnblogs.com/dotnetcrazy/p/9175950.html#4.5.可变类型和不可变类型

对于可变不可变系列就不去复述了,下面再来几个案例看看 值判断地址判断的概念

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基础系:https://www.cnblogs.com/dotnetcrazy/p/9102030.html#9.1.Python

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.列表生成式

如果下面知识点还不熟悉的,看看之前讲的~列表生成式:https://www.cnblogs.com/dotnetcrazy/p/9155310.html#1.9.列表生成式

简写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)会递归复制

PS:我们常用的切片相当于浅拷贝copy.copy()

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、1、2、3、5、8、13、21、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]
 
aaarticlea/png;base64,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
" alt="" />
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);

这部分不是很难,简单提一下知识点即可。如果你想深究可以==> ( )

在收获满满一箩筐眼球后,小明拍拍屁股去了新开的饭店大吃一顿了...


写在最后:还有一些内容没写,估计过几天又有一篇叫 “基础拓展” 的文章了,为啥不一起写完呢?

其实逆天也想写完,真写完文章又被叫做长篇大论一百页了 #^_^# 行了,听取大家意见,不写那么长的文章,下次见~

Python3 与 C# 扩展之~基础衍生的更多相关文章

  1. 在TypeScript中扩展JavaScript基础对象的功能

    最近工作中用到,记录一下:假设我们需要一个功能,把一个数字比如10000输出为下面的字符串格式“10,000”,一般是写一个方法,那么我希望更方便一点,直接向Number类型添加一个格式化方法,比如叫 ...

  2. 了解Java密码扩展的基础

      了解Java密码扩展的基础     Java密码扩展(The Java Cryptography Extension),是JDK1.4的一个重要部分,基本上,他是由一些包构成的,这些包形成了一个框 ...

  3. Python3 与 C# 扩展之~基础拓展

      上次知识回顾:https://www.cnblogs.com/dotnetcrazy/p/9278573.html 代码裤子:https://github.com/lotapp/BaseCode ...

  4. Python3 与 C# 扩展之~模块专栏

      代码裤子:https://github.com/lotapp/BaseCode/tree/maste 在线编程:https://mybinder.org/v2/gh/lotapp/BaseCode ...

  5. 一起来做chrome扩展《基础介绍》

    首先说明,chrome的扩展并不它的插件,网上很多说写插件,其实都是说的扩展.写扩展并不复杂,只要根据chrome提供的一系列的API进行就可以实现很多的功能.只是对API的学习是有代价的,加上国内访 ...

  6. python3.x 学习笔记1(基础知识)

    1.python模块: 标准库和第三方库,第三方库需要下载安装 2.模块sys: 命令 功能 sys.stdin 标准输入流sys.stdout  标准输出流sys.stderr           ...

  7. python3 速查参考- python基础 8 -> 面向对象基础:类的创建与基础使用,类属性,property、类方法、静态方法、常用知识点概念(封装、继承等等见下一章)

    基础概念 1.速查笔记: #-- 最普通的类 class C1(C2, C3): spam = 42 # 数据属性 def __init__(self, name): # 函数属性:构造函数 self ...

  8. Chrome扩展开发基础教程(附HelloWorld)

    1 概述 Chrome扩展开发的基础教程,代码基于原生JS+H5,教程内容基于谷歌扩展开发官方文档. 2 环境 Chrome 88.0.4324.96 Chromium 87.0.4280.141 B ...

  9. Python3学习(一)-基础、数据类型、变量、字符串和编码、list&tuple、if、for、while、dict、set、函数与参数

    ##廖雪峰py3笔记 ## '//'这是获得相除后的整数部分 ##a = 10//3 ##print (a) ## '/'获得相除后的结果,为浮点数,结果能整除也也是浮点数 ##b = 10/3 ## ...

随机推荐

  1. JavaScript生成二维码图片

    1.引入一个二维码工具的js文件,同时需要引入jquery文件 下面是jquery.qrcode.min.js文件内容: (function(r){r.fn.qrcode=function(h){va ...

  2. Oracle Profile 配置文件

    Profile是用户的配置文件,它是密码限制,资源限制的命名集合.利用profile 可以对数据库用户进行基本的资源管理,密码管理. 1 创建profile 的语法 create profile pr ...

  3. Jquery模拟多选框(checkbox)

    代码如下: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <tit ...

  4. (二)Wireshark的实用表格

    主要内容: 1.了解端点概念,学习如何在Wireshark中查询端点信息 2.学习利用端点窗口与会话窗口来分析数据包的特点 3.学会Wireshark的协议分层统计窗口的用法 一.端点概念 和数学里的 ...

  5. Hadoop2.0 Namenode HA实现方案

    Hadoop2.0 Namenode HA实现方案介绍及汇总 基于社区最新release的Hadoop2.2.0版本,调研了hadoop HA方面的内容.hadoop2.0主要的新特性(Hadoop2 ...

  6. 排查 Maxwell can not find database 并且使用 MySQL binlog 解决相关问题

    目前我们在使用 Maxwell 在读线上机器的 binlog 同步我们的离线数据库. 这次错误定位上,首先线要确定问题是发生在生产者 还是队列 还是消费者.经过查看各机器上任务的运行日志,定位到了问题 ...

  7. 压测工具使用(vegeta)

    一.压测工具vegeta 1.介绍 Vegeta 是一个用 Go 语言编写的多功能的 HTTP 负载测试工具,它提供了命令行工具和一个开发库. 官方地址:https://github.com/tsen ...

  8. MyBatis SpringBoot 杂记

    最近接了个xxx代码. 不能说人家不好, 因为必进年月久了.能用这么长时间, 不就说明还不错么?! 我们现在每天写的, 能超出人家的么~~~ 呵呵 Java项目中, 把动态数据源切换的框架整合进来. ...

  9. Deploy .NET Core with Docker

    Creating a .NET Core project If you already have an existing .NET Core project you are more than wel ...

  10. vuex2.0 基本使用(4) --- modules

    vue 使用的是单一状态树对整个应用的状态进行管理,也就是说,应用中的所有状态都放到store中,如果是一个大型应用,状态非常多, store 就会非常庞大,不太好管理.这时vuex 提供了另外一种方 ...