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

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

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()得到的地址一样则相同)

== 是比较两个对象的值是否相等

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

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循环遍历的,就是可迭代的

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

简写: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中赋值、浅拷贝、深拷贝¶

看到标题小明和小潘就楞了,老师当时没讲解啊,然后两个人眼巴巴的看着老师讲解:

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.赋值¶

赋值方法和Python一样,直接赋值即可

var list1 = new List() { 1, 2, 2 };

var list2 = list1;

In [68]:

%%script csharp

// Python一样,直接赋值即可

var list1 = new List() { 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() { 1, 2, 2 };

var list4 = new List(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再复杂点的,上面的方式就变成浅拷贝了:(类似于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(){

new Student { Name = "小张", Age = 22 },

new Student { Name = "小明", Age = 23 }

};

var p = new Student() { Name = "小潘", Age = 23 };

list5.Add(p);

// 浅拷贝一份

var list6 = new List(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实现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定义一个扩展方法:(温馨提醒:扩展方法所在的类必须是static Class哦)

public static partial class ListExt

{

// 只要T实现了ICloneable接口就可以了

public static IEnumerable DeepCopy(this IEnumerable list) where T : ICloneable

{

return list.Select(item => (T)item.Clone()).ToList();

}

}

来个调用加验证:

#region 引用类型深拷贝-简单实现方式

var oldList = new List(){

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(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(){

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]:

at 0x7f14c413cb48>

遍历方式可以用之前的for循环来遍历(推荐)

也可以用next()或者__next__()方法来遍历。【C#是用MoveNext】

generator保存的是算法,每次调用next(xxx)或者__next__(),就计算出下一个元素的值,直到计算到最后一个元素

当没有更多的元素时,抛出StopIteration的异常

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)

in ()

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停下了,一直到最后

生成器的特点:

节约内存

迭代到下一次的调用时,所使用的参数都是第一次所保留下的(所有函数调用的参数都是第一次所调用时保留的,而不是新创建的)

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]:

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的循环的时候

小明总结如下:

在Python中,这种一边循环一边计算的机制称为生成器:generator

每一个生成器都是一个迭代器(迭代器不一定是生成器)

如果一个函数包含yield关键字,这个函数就会变为一个生成器

生成器并不会一次返回所有结果,而是每次遇到yield关键字后返回相应结果,并保留函数当前的运行状态,等待下一次的调用

由于生成器也是一个迭代器,那么它就支持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]:

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)

in ()

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.扩展之~return和break的说明¶

在一个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时,发现拿不到generator的return语句的返回值

如果想要拿到返回值,必须捕获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)

in ()

7 c.send("小潘")

8 # 生产结束,通知消费者结束,抛出StopIteration异常

----> 9c.send(None) # 使用c.close()可以避免异常

StopIteration:

执行流程:

创建协程对象(消费者)后,必须使用send(None)或__next__()启动

协程在执行yield后让出执行绪,等待消息

调用方发送send(msg)消息,协程恢复执行,将接收到的数据保存并执行后续流程

再次循环到yield,协程返回前面的处理结果,并再次让出执行绪

直到关闭或被引发异常

补全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)

in ()

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.Iterable 转 Iterator¶

生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator

把list、dict、str等Iterable变成Iterator可以使用iter()函数:

In [103]:

iter(a)

Out[103]:

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等则不行

小明总结了一下老师讲解的知识点:

可以for循环的对象都是Iterable类型

可以使用next()or__next__()函数的对象都是Iterator类型

集合数据类型如list、dict、str等是Iterable,可以通过iter()函数获得一个Iterator对象

7.CSharp迭代器¶

乘着下课的时间,小明跑到黑板前,心想:“又到了C#的时候了,看我来收播一大群眼球~”,然后开始了他的个人秀:

其实迭代器(iterator)就是为了更简单的创建枚举器(enumerator)和可枚举类型(enumerator type)的方式

7.1.IEnumerator 和 IEnumerable¶

通俗话讲:

能不能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

{

///

/// 需要遍历的数组

///

private string[] array;

///

/// 有效数的个数

///

private int count;

public MyEnumerator(string[] array, int count)

{

this.array = array;

this.count = count;

}

///

/// 当前索引(线moveNext再获取index,用-1更妥)

///

private int index = -1;

public object Current

{

get

{

return array[index];

}

}

///

/// 移位

///

///

public bool MoveNext()

{

if (++index < count)

{

return true;

}

return false;

}

///

/// 重置

///

public void Reset()

{

index = -1;

}

}

MyArray.cs文件

public partial class MyArray

{

///

/// 数组容量

///

private string[] array = new string[4];

///

/// 数组元素个数

///

private int count = 0;

///

/// 当前数组的长度

///

public int Length

{

get

{

return count;

}

}

///

/// 添加元素

///

///

///

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;

}

///

/// 移除某一项

///

///

///

public MyArray RemoveAt(int i)

{

for (int j = i; j < count - 1; j++)

{

array[j] = array[j + 1];

}

count--;//少了一个元素所以--

return this;

}

///

/// 索引器

///

///

///

public string this[int index]

{

get

{

return array[index];

}

set

{

array[index] = value;

}

}

}

MyArrayExt.cs文件:

public partial class MyArray: IEnumerable

{

///

/// 枚举器方法

///

///

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)的方式

如果枚举器和可枚举类型还是不理解(举个例子)就懂了:(从遍历方式就看出区别了)

定义一个斐波拉契函数,返回可枚举类型

///

/// 返回一个可枚举类型

///

public static IEnumerable 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);

}

定义一个斐波拉契函数,返回一个枚举器

///

/// 返回一个枚举器

///

public static IEnumerator 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

{

///

/// 数组容量

///

private string[] array = new string[4];

///

/// 数组元素个数

///

private int count = 0;

///

/// 当前数组的长度

///

public int Length

{

get

{

return count;

}

}

///

/// 添加元素

///

///

///

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;

}

///

/// 移除某一项

///

///

///

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;

}

///

/// 索引器

///

///

///

public string this[int index]

{

get

{

return array[index];

}

set

{

array[index] = value;

}

}

}

MyArrayExt.cs

public partial class MyArray : IEnumerable

{

///

/// 枚举器方法

///

///

public IEnumerator GetEnumerator()

{

return MyEnumerator();

}

///

/// 通过yield快速实现

///

///

public IEnumerator 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 break,C#使用yield break来退出迭代

做个 demo 测试下:

public static IEnumerable 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()函数

.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 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 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不理解就看看定义就懂了:public delegate TResult Func(T arg);

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

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

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

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

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

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

了解Java密码扩展的基础

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

Python3 与 C&num; 扩展之~基础拓展

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

Python3 与 C&num; 扩展之~模块专栏

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

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

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

python3&period;x 学习笔记1&lpar;基础知识&rpar;

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

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

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

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

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

【python3 自动化之mysql操作】python3下的mysql入门基础

1.所需资源:pycharm,python3.6,module:pymysql 2.pycharm配置mysql: 新添加一个mysql数据库 ip:192.168.112.54    端口:3306 ...

随机推荐

深入理解CSS3 Animation 帧动画

CSS3我在5年之前就有用了,包括公司项目都一直在很前沿的技术. 最近在写慕课网的七夕主题,用了大量的CSS3动画,但是真的沉淀下来仔细的去深入CSS3动画的各个属性发现还是很深的,这里就写下关于帧动 ...

C&sol;S端开发问题汇总

0.先推荐几款工具,连接远程客户端DameWare Mini Remote Control,搜索本地文件Everything,以及sysinternals的系列工具: FileMon-监视所有文件修改 ...

chkconfig &colon; No such file or directory

sys_version:12.04LTS For example: #chkconfig --level mysql on /sbin/insserv:No such file or director ...

OAuth 2&period;0 开发完全详解

--------------------------基础篇------------------------------- I:OAuth 2.0 概述 首先大家来看看国内新浪跟腾讯这两大头对OAuth ...

一大坨GoogleAPI的学习笔记之一(oAuth2&period;0)

官方文档地址:https://developers.google.com/accounts/docs/OAuth2InstalledApp 最近Ubuntu下VGA接口无端的不能用了,木有心情翻译了, ...

链接脚本之LMA VMA解释

链接脚本中的LMA和VMA是什么意思.这个问题纠结了一段时间,今天在看时,豁然开朗,写下自己的认识.分享例如以下: LMA:载入地址 位于存储器中的地址  LOAD ...

转delphi中 formclose的事件 action&colon;&equals;cafree form&colon;&equals;nil分别是什么意思?

转自:http://www.cnblogs.com/jshchg/articles/1929894.html MDI子窗体关闭时用到的(以下摘自Delphi的帮助)caNone  The form i ...

挖洞姿势:特殊的上传技巧,绕过PHP图片转换实现远程代码执行(RCE)

我使用了一个特殊的图片上传技巧,绕过PHP GD库对图片的转换处理,最终成功实现了远程代码执行. 事情是这样的.当时我正在测试该网站上是否存在sql注入漏洞,不经意间我在网站个人页面发现了一个用于上传 ...

6&period;Solr4&period;10&period;3API使用&lpar;CURD&rpar;

转载请出自出处:http://www.cnblogs.com/hd3013779515/ 1.在工程中引入solr-solrj-4.10.3.jar

八&period;linux系统文件属性知识

1.文件属性权限是12位,现在只看9位,其中每3个一组,为:属主权限.属组权限.其他权限,其中r可读,w可写,x可执行,如图: 2.文件属性之软硬链接   linux系统中有两种链接,为硬链接(ln) ...

python import 类如何捕获clrt c_Python3 与 C# 扩展之~基础衍生相关推荐

  1. python import 类如何捕获clrt c_PEP8 python规范神器

    一.Jupyter notebook 篇 Jupyter notebook的代码要想写得规范,推荐用Code prettify插件. 1.安装插件Nbextensions 1 pip install ...

  2. python import 类 继承_python学习之类的继承

    面向对象中一个重要的特性就是继承,继承的好处就是提高代码的重用率,减少不必要的代码.继承是父类与子类的关系,当子类继承了父类后,就具有了父类的所有变量和方法.在python中定义继承的语法是:clas ...

  3. python日志类logging的使用

    python日志类logging的使用 文章目录 python日志类logging的使用 1.简单使用 2.设置日志级别 3.格式化输出日志 4.一个常用的例子和handler的使用 5 捕获 Tra ...

  4. python定义类()中写object和不写的区别

    python定义类()中写object和不写的区别 这里需要说明一下: python3中,类定义默认继承object,所以写不写没有区别 但在python2中,并不是这样 所以此内容是针对python ...

  5. python import from class_Python: import vs from (module) import function(class) 的理解

    Python: Import vs From (module) import function(class) 本文涉及的 Python 基本概念: Module Class import from . ...

  6. Python -- dict 类

    Python dict类常用方法: class dict(object): def clear(self):  #清除字典中所有元素形成空字典,del是删除整个字典: >>> tes ...

  7. Python动态类和动态方法的创建和调用

    借助于python的动态语言特性,很容易对对象进行添加方法或者属性,这也是python的灵活之一. 动态生成类的属性及其方法 在某些情况可能要根据不同的参数来动态生成不同的实例方法.静态方法.类方法. ...

  8. import的用法python_Python导入模块,Python import用法(超级详细)

    Python导入模块,Python import用法(超级详细) 使用 Python 进行编程时,有些功能没必须自己实现,可以借助 Python 现有的标准库或者其他人提供的第三方库.比如说,在前面章 ...

  9. python数据库连接类写其他类调用报超时_python面向对象,类,以及连接数据库

    ## 面向对象 ### 1.面向对象三大特性: 封装,继承,多态 ### 1.1.封装 1.在类中对数据的赋值.内部调用对外部用户是透明的 2.这使类变成了一个胶囊或容器,里面包含着类的数据和方法 3 ...

最新文章

  1. 插值搜索——本质和二分无异,是利用数据分布的规律来定查找点,其基本假设是数据分布均匀...
  2. 『Python』VS2015编译源码注意事项
  3. Linux 访问文件的acl信息,linux文件权限管理与ACL访问控制列表
  4. 直播马上开始│走进腾讯云物联网
  5. SAP Spartacus ComponentData的提前subscription
  6. redis存储新闻列表_Redis对象——集合(Set)
  7. IOS开发之格式化日期时间的使用 编程中常见问题
  8. Django周总结一
  9. python爬虫案例——csdn数据采集
  10. 利用flashback database实现部分对象回滚
  11. MySQL数据以全量和增量方式,同步到ES搜索引擎
  12. 2020中兴捧月算法大赛 埃德加考特派 区域优胜奖源代码
  13. 应变式传感器的原理与优点
  14. 阿里云盘 WebDAV升级
  15. 网络运维中静态路由 三层交换技术 动态路由 OSPF协议的配置
  16. python ——时间间隔
  17. dmd oracle,【智能科技学院】TF-SWUFE Oracle Club DMD方向召开第五周核心成员分享会...
  18. 什么是MES生产制造执行系统?实施MES生产管理系统有哪些目标?
  19. 元宇宙,现实与虚拟交互的新一代互联网?
  20. 2011年3月《震痛•震恫•震动》

热门文章

  1. 日料美食海鲜精品海报PSD分层模板,美味势不可挡
  2. python能做页面加载动画吗_HTML+CSS实现页面加载(loading)动画效果
  3. C++ 虚析构函数作用
  4. Windows下消息队列优先级顺序(转载)
  5. JavaBean规范、EL、JSTL、
  6. Execution Environment for Non-64-bit Modes 和 64-bit Modes
  7. RAN adjusts schedule for 2nd wave of 5G specifications
  8. 2018.10.22 20:10
  9. 免费制作微信小程序开发关于旅游_教大家怎么一步步免费自己做微信小程序
  10. php7.2连接mysql8_兼容 php 7.2 及 mysql 8