BigSmall
发表于 2018-11-4 14:48:12
本帖最后由 BigSmall 于 2018-11-5 11:17 编辑
43
工厂函数就是类对象
len()和dir()是BIF
而int()、list()和我们之前定义的类,这些都是工厂函数,即类对象(type)
int()里面的加减方法可以被修改,加法变减法,或者变成其他的
有关算术的魔法方法
44
Python 魔法方法详解
https://fishc.com.cn/forum.php?mod=viewthread&tid=48793&extra=page%3D1%26filter%3Dtypeid%26typeid%3D403
#43和44都是讲算术运算的,所以这里将他们写在一块,还有一个重要的原因跳过了45课。{:10_262:}
BigSmall
发表于 2018-11-4 16:01:50
本帖最后由 BigSmall 于 2018-11-6 21:53 编辑
45
简单定制
定制一个计时器的类
准备:
使用time模块的localtime方法获取两个时间,然后时间相减,就会得到时间间隔
localtime输出的格式不太好理解(会很麻烦),所以就改用time.time()函数
time模块详解:https://fishc.com.cn/forum.php?mod=viewthread&tid=51326&extra=page%3D1%26filter%3Dtypeid%26typeid%3D403
__str__:如果要把一个类的实例变成 str,就需要实现特殊方法__str__(),就是类实例化后就是一个字符串,类里面通过return函数返回字符串
__repr__:类实例化后直接打印一个字符串
代码:
timer1.0:相当好理解,里面有很多bug,具体优化见timer2.0
import time as t
class Mytimer():
#计时开始
def start(self):
self.starttime=t.time() #这里time.time()函数是将现在的时间转换成秒(从1970年开始算),小数点后还有好几位
print('计时开始')
#计时结束
def stop(self):
self.stoptime=t.time()
print('计时结束')
#计算时间间隔
def count(self):
self.duringtime=int(self.stoptime-self.starttime) #之前说了,这里小数点后有好几位,所以用了int转换成整数
print('运行了'+str(self.duringtime)+'s')
timer2.0:优化了bug
import time as t #注意我们这里将time模块用t来表示,如果在后面的运行的时候,实例化的时候如果名字是t,就会出问题,尽量使实例化的名字不要跟我们写的程序代码一样
class Mytimer():
def __init__(self):
self.duringtime='未开始计时' #如果开始就直接调用t1,就显示'未开始计时',没有这句就会报错;如果属性的名字与函数的名字一样,那么函数就会被属性替换
self.starttime=0 #为后的if语句服务
def __repr__(self): #repr函数是将实例化的类转化成字符串,并打印出来;这里是当直接输入t1,那么就会直接输出多少秒,不用执行count函才输出,或者是执行stop函数就会输出
return(self.duringtime)
def __add__(self,other):
self.totaltime='一共运行了'+str(int(self.stoptime-self.starttime+other.stoptime-other.starttime))+'s' #之前这里我还是用的self.duringtime,因为我还是想的不return,直接repr打印,可是不行
return(self.totaltime) #这里为什么非得用return呢?因为你在写t1+t2时,看到+,会直接调用t1的__add__,所以必须return;而我们一般用的+,就是算术的直接加,这里是特殊的,不能直接加
#计时开始
def start(self):
self.starttime=t.time()
print('计时开始')
self.duringtime='未结束计时' #这个是当运行了start函数,如果输入t1,那么会提醒你;运行t1打印的是self.duringtime,这里提前给self.duringtime赋值了
#计时结束
def stop(self):
if not self.starttime: #当not self.duringtime不等于0(即self.duringtime=0),那么就没有经过start的赋值语句,说明没有计时
print('未开始计时')
else:
self.stoptime=t.time()
print('计时结束')
self.count() #在函数里面可以直接调用函数的另一个函数,写法就是这里的self.函数名();调用该函数就不需要再调用count函数了,直接进行计算
#计算时间间隔
def count(self):
self.duringtime='运行了'+str(int(self.stoptime-self.starttime))+'s' #这里为什么都用字符串呢,因为repr函数只会打印字符串,所以在这里强制转换了
#本来这里有个print,可是有了repr函数,就不需要了;repr直接调用,比之前的调用count函数更符合计时器
BigSmall
发表于 2018-11-4 17:11:58
本帖最后由 BigSmall 于 2018-11-5 10:47 编辑
46
之前学的是魔法方法来改变一些函数方法,这节课是改变属性
第一个例子:
class C:
def __init__(self,size=10):
self.size=size
def getSize(self):
return self.size
def setSize(self,value):
self.size=value
def delSize(self):
del self.size
x=property(getSize,setSize,delSize)
接下来,c.x会直接调用getSize;c.x=1会直接调用setSize;del c.x会调用delSize
这是因为这些set还有get之类的方法都是描述符,根据你的操作实现对应功能。下面那个列子也一样
第二个例子:
class Test:
def __init__(self,width=0,height=0):
self.width=width
self.height=height
def __setattr__(self,name,value):
if name=='a':
self.width=value
self.height=value
else:
self.name=value
__setattr__这个是设置属性,每当有self的属性进行赋值操作时,就会被调用
操作:
test1=Test(1,2)
这就会陷入无限的循环,首先__init__里面width赋值操作,setattr被调用,name是width,不是a,执行else-self.name赋值操作,继续调用setattr,所以无限循环
修正:
将self里面的self.name=value改为super().__setattr__(name,value),意思一样,就是防止了无限循环
到else时,name不是a,就为name赋值value,即为width赋值width
然后init里面的height赋值,继续调用setattr,后面的就同上了
BigSmall
发表于 2018-11-5 08:14:24
本帖最后由 BigSmall 于 2018-11-5 10:47 编辑
47
描述符
描述符就是 将某种特殊类型的类的实例指派给另一个类的属性。简单的说就是在一个新类里面放入实例化的特殊类。
有关的魔法方法有以下三种:
__get__(self,instance,owner):
用于访问属性,它返回属性的值。
__set__(self,instance,value):
将在属性分配操作中调用,不返回任何内容。
__delete__(self,instance):
控制删除操作,不返回任何内容。
所谓特殊类型的类,就是含有以上三种魔法方法的类。然后把这个特殊的类的实例,指派给另一个类的属性
对于特殊类型的类,进行赋值操作,暂且认为先__init__有了初值,然后__set__设置了新值
温度华氏度与摄氏度之间的转换
class Sheshidu:
def __init__(self,value=26):#__init__函数不写也可以执行set,但是get无法传入value,而且必须为value赋初值,否则__init__执行不下去
self.value=float(value)#float是为了转换时出现小数
def __get__(self,instance,owner):#模板写法
return(self.value)
def __set__(self,instance,value):#模板写法
self.value=float(value)
class Huashidu:
def __get__(self,instance,owner):
return(instance.shishidu*1.8+23)#在华氏度与摄氏度之间建立关系,通过输入摄氏度转换成华氏度,instance实现两个特殊类的关联
#输入wendu.shishidu=1,此时进入shishidu的get,然后输入wendu.huashidu,进入huashidu的get,再进入instance.shishidu,这样就实现了转换
def __set__(self,instance,value):
instance.shishidu=(float(value)-32)/1.8#在华氏度与摄氏度之间建立关系,通过输入华氏度转换成摄氏度,实现原理如上
class Wendu:
shishidu=Sheshidu()
huashidu=Huashidu()
BigSmall
发表于 2018-11-5 10:43:33
本帖最后由 BigSmall 于 2018-11-5 10:53 编辑
BigSmall 发表于 2018-11-5 08:14
47
描述符
描述符就是 将某种特殊类型的类的实例指派给另一个类的属性。简单的说就是在一个新类里面放入 ...
47课课后习题
https://www.jianshu.com/p/d3b41de6a91a
#以后有什么补充内容还是修改写入帖子里面吧,写在回复里面不方便看
BigSmall
发表于 2018-11-7 08:48:53
本帖最后由 BigSmall 于 2018-11-7 19:56 编辑
48
定制容器
协议:一种指南
诸如列表、元组和字典,这些都是容器,里面存放各种各样的对象
容器类型的协议:
如果你定制的容器不可变,如元组、字符串,你只需要定义__len__()和__getitem__()方法
如果你希望定制的容器是可变的,除了上述两种方法外,还需要定义__setitem__()和__delitem__()方法
容器类型的魔法方法
__len__(self):定义当被 len() 调用时的行为(返回容器中元素的个数)
__getitem__(self, key):定义获取容器中指定元素的行为,相当于 self(返回列表元素,并实现你需要的功能)
__setitem__(self, key, value):定义设置容器中指定元素的行为,相当于 self = value
__delitem__(self, key):定义删除容器中指定元素的行为,相当于 del self
__iter__(self):定义当迭代容器中的元素的行为
__reversed__(self):定义当被 reversed() 调用时的行为
__contains__(self, item):定义当使用成员测试运算符(in 或 not in)时的行为
练习:编写一个不可改变的自定义列表,要求记录列表中每个元素被访问的次数
准备:1.*args:(表示的就是将实参中按照位置传值,多出来的值都给args,且以元祖的方式呈现)
def foo(x,*args):
print(x)
print(args)
foo(1,2,3,4,5) #其中的2,3,4,5都给了args
1
(2, 3, 4, 5) #这里args其实是一个元组
2.列表推导式提供了一种创建list的简便方法。应用程序创建列表时,列表中的元素来源于其他序列、可迭代对象或创建的一个满足一定条件的序列。
self.values= #其实就是创建了elf.values这个列表,列表里面的元素是x,x是for循环遍历元组args
3.统计每个元素被访问的次数,最先想到的是什么,是字典,将这个列表的下标作为这个字典的键,它的值就是它对应的访问次数
4.字典的创建:{}.fromkeys(包含键的列表,键的初值)
5.range(3)就是创建一个列表
BigSmall
发表于 2018-11-7 20:25:47
本帖最后由 BigSmall 于 2018-11-8 16:11 编辑
48课后题
0.首先,序列是是Python中最基本的数据结构。序列中的每个元素都分配一个数字 - 它的位置,或索引,第一个索引是0,第二个索引是1,依此类推。每个索引对应一个元素。
字典不是序列,他是python中唯一的一个映射类型
4.容器的一些操作:读 —— __getitem__(),写 —— __setitem__(),删除 —— __delitem__()
5.在 Python 中,协议更像是一种指南。这有点像我们之前在课后作业中提到的“鸭子类型”(忘了的朋友请戳:http://bbs.fishc.com/thread-51471-1-1.html) —— 当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。Python就是这样,并不会严格地要求你一定要怎样去做,而是让你靠着自觉和经验把事情做好!所以python就没那么正式。
return之后的语句不会被执行
0.定制一个列表,同样要求记录列表中每个元素被访问的次数
要求1:实现获取、设置和删除一个元素的行为(删除一个元素的时候对应的计数器也会被删除)
要求2:增加 counter(index) 方法,返回 index 参数所指定的元素记录的访问次数
要求3:实现 append()、pop()、remove()、insert()、clear() 和 reverse() 方法(重写这些方法的时候注意考虑计数器的对应改变)
自己写的,比较笨,看不懂答案里面的super()。#这里我们用list2表示实例化后的列表
class Mylist:
def __init__(self,*args):
self.newlist=
self.countlist=
#init就是准备工作,这里要定制一个列表,要求记录列表中每个元素被访问的次数。想法就是用两个列表,一个来存放元素,一个来返回元素被访问的次数。
def __repr__(self):
return str(self.newlist)
#repr,运行的时候当输入list2(实例化的)的时候,可以直接返回想要的列表,这里我们newlist是存放元素的,所以这里我们返回这个
def __len__(self):
return len(self.newlist)
def __getitem__(self,listkey):
self.countlist+=1
return self.newlist
#get函数就是读,比如我们输入list2时,表示我们读取list2中位置1的元素,此时list2就被访问一次,访问次数加一
def __setitem__(self,listkey,value):
self.countlist+=1
self.newlist=value
#set就是写,就是将列表中某个位置的元素重写,此时也算访问一次
def __delitem__(self,listkey):
del self.newlist
del self.countlist
#del就是删除,我们这里不仅要删除元素,而且还要删除元素对应的访问次数
#因为我们写的newlist就是一个列表,delitem函数的实现就是要删除newlist与countlist,那就直接调用这两个列表的del函数就可以了,即del self.countlist
def counter(self,listkey):
return self.countlist
#上面三个函数的实现都是直接调用的,不用‘列表名.方法’这样来调用,但是之后的就不一样了
def append(self,value):
self.countlist.append(0)
self.newlist.append(value)
def pop(self,listkey):
print(self.newlist) #这里为什么用print,因为return实现不了
del self.newlist
del self.countlist
def remove(self,value):
key=self.newlist.index(value)
del self.newlist
del self.countlist
def insert(self,listkey,value):
self.newlist.insert(listkey,value)
self.countlist.insert(listkey,0)
def clear(self):
self.newlist.clear()
self.countlist.clear()
def reverse(self):
self.newlist.reverse()
self.countlist.reverse()
BigSmall
发表于 2018-11-7 20:44:42
本帖最后由 BigSmall 于 2018-11-12 19:44 编辑
49迭代器
迭代就是重复一个操作
递归就是自己调用自己
列表、元组、字符串都是可迭代对象
for i in 序列
diedaiqi1=iter(可迭代对象);生成迭代器
next(diedaiqi1);比如字符串,会依次返回字符串的值
如果迭代到没有元素,那就会报错;StopIteration
两个魔法方法:
__iter__();返回本身
__next__();迭代要实现的内容
ssxx43
发表于 2018-11-8 19:19:22
楼主每天在哪看课程学习的,看小甲鱼的零基础入门学py?还是b站上的视频?
BigSmall
发表于 2018-11-10 14:48:34
ssxx43 发表于 2018-11-8 19:19
楼主每天在哪看课程学习的,看小甲鱼的零基础入门学py?还是b站上的视频?
在b站上看小甲鱼
BigSmall
发表于 2018-11-12 20:15:40
本帖最后由 BigSmall 于 2018-11-13 13:53 编辑
50生成器
生成器仅需要普通的函数就可以实现。生成器是迭代器的一种实现,迭代器需要我们定义一个类,而生成器只要在简单的函数里面加上yield语句。
yield相当于一个return,执行到yield返回值并跳出函数,下一次迭代直接运行之后的内容
协同程序就是可以运行的独立函数调用,函数可以暂停或者挂起,并在需要的时候从程序离开的地方继续或者重新开始
用for语句执行迭代器,可以识别StopIteration,当执行到StopIteration直接退出循环
(推导式)是生成器推导式;e=(i%2==0),此时e就是一个生成器,执行的就是打印能整除2的数
扩展阅读;https://fishc.com.cn/forum.php?mod=viewthread&tid=56023&extra=page%3D1%26filter%3Dtypeid%26typeid%3D403
BigSmall
发表于 2018-11-12 20:40:23
51模块
模块就是更高级的封装
封装:
容器-数据的封装;列表,元组,字符床,字典
函数-语句的封装
类-方法和属性的封装,也就是对函数和数据的封装
模块-程序;也就是我们写的.py文件,模块要和你要import的文件放在同一目录
命名空间:程序.函数
%后跟.2;意思就是保留小数点后两位小数
导入模块
import 模块名;调用的时候需要模块名.函数名
from 模块名 import 函数名;调用的时候直接函数名
from 模块名 import *;导入所有的函数,不推荐这么做,名字混乱,文件自身的和模块里面名字相同的会被覆盖
import 模块名 as 新名字;最推荐的
BigSmall
发表于 2018-11-12 21:09:20
52模块
模块里面可以调用自己的函数,这样运行就会直接执行该函数,通常测试函数这么做
if __name__== '__main__';就是有些函数(测试函数)不希望出现在其他函数的调用中,就在模块中将调用函数的的语句写在if __name__== '__main__'里面,这样就只会出现在该模块的执行中
搜索路径:python有自己默认的路径,它会在默认的路径里面搜索模块,一般推介放在’site-packages‘里卖弄,不过我们也可以将我们需要的路径添加到默认的路径里面。sys.path.append("添加路径")
,这里sys.path是默认的路径,是一个列表。
包;就是整理模块
第一步,创建一个文件加,用于存放相关的模块,文件夹的名字即包的名字
第二步,在包文件夹中创建一个__init__.py的模块文件,内容可以为空
第三步,将相关的模块放到该文件夹中
导入的时候,包名.模块名
BigSmall
发表于 2018-11-12 21:44:53
53
不开心,不小心点了发帖,之前记的本节课的笔记也丢失了,21:44,算了,明天再说吧
BigSmall
发表于 2018-11-13 12:50:03
本帖最后由 BigSmall 于 2018-11-13 13:55 编辑
53像个极客一样去思考
使用现成的模块来工作
python标准库有数百个模块
打开python文档;idle-help-python docs
与计算机相关的单词语法;每天学一点英语
模块社区;http://pypi.python.org/pypi
如何独立的探索模块
计时器模块-timeit
helip-索引timeit;需要阅读长篇的英文资料
idle-import timeit-timeit.__all__(all精简完就是可以供外界调用的所有信息)
time.__flie__,源代码位置
help(timeit);复杂度介于中间的方法
timeit文档翻译;https://fishc.com.cn/forum.php?mod=viewthread&tid=55593&extra=page%3D1%26filter%3Dtypeid%26typeid%3D403
BigSmall
发表于 2018-11-15 21:41:46
本帖最后由 BigSmall 于 2018-12-7 21:47 编辑
54论一只爬虫的自我修养
python如何访问互联网
urllib
url;就是平常说的网页地址
url的一般格式;协议://域名(有时候要包含端口号,即:端口号)/路径
端口号;协议都有默认的端口号,http的默认端口为80
协议有http、https、ftp、file、ed2k
urllib其实是一个包,里面有四个模块
urllib.request模块;urlopen(字符串)函数访问网页
例子:
import urllib.request as a
b=a.urlopen('网站地址')
c=b.read()
print(c)
c=decode('utf-8')
BigSmall
发表于 2018-11-18 19:33:29
本帖最后由 BigSmall 于 2018-12-7 21:59 编辑
55实战
例1
import urllib.request as req #导入访问网页的模块
i=400
while i<=1000:
a='https://placekitten.com/'+str(i)+'/'+str(i)
cat=req.urlopen(a) #访问网页
cat_pic=cat.read() #读取网页,并赋值给cat_pic(值是二进制的)
with open ('cat'+str(i)+'.jpg','wb') as f: #创建一个cat.jpg的文件类f(二进制的写)
f.write(cat_pic) #将二进制的值写入f类
i+=50
注意;这里‘’里面是不能直接写变量的,需要将变量str
例2
Network标签页
Network标签页对于分析网站请求的网络情况、查看某一请求的请求头和响应头还有响应内容很有用,特别是在查看Ajax类请求的时候,非常有帮助。注意是在你打开Chrome开发者工具后发起的请求,才会在这里显示的哦。
点击左侧某一个具体去请求URL,可以看到该请求的详细HTTP请求情况:
我们可以在这里看到HTTP请求头、HTTP响应头、HTTP返回的内容等信息,对于开发、调试,都是很有用的。
简单地说,就是当网页运行的时候,客户端发出请求,然后服务器执行的方法和内容
方法中的get和post都可以向服务器提交数据
点开方法,preview中可以查看是否实现了想要的功能,headers中可以查看方法实现的具体内容
request是网络请求
request header是发出请求的客户端,其中的user agent可以查看你是用什么系统、什么软件访问的,很多网页拒绝非人类访问,就是在这里监察访问的软件。但是这样依然阻挡不了我们代码访问,这里的user agent是可以进行修改的。
form date是表张数据,就是post方法提交的主要的内容
python的urlopen(url字符串,date);date默认的是get方法,要是需要的话可以传入值,改为post方法。但是date是有特定的格式的,这里我们可以用函数urllib.parse.urlencode()来将字符串或者字典转换成这个格式。而且urllib.parse.urlencode()这个函数是在urllib包里面的独立的模块urllib.parse
urlencode默认的是Unicode,有时候需要encode为utf-8编码形式;encode(utf-8)。相对的,如果你想将utf-8的编码转为Unicode的形式;decode(utf-8)
如果对编码还有什么问题的话,https://fishc.com.cn/forum.php?mod=viewthread&tid=56452&highlight=python%B1%E0%C2%EB%CE%CA%CC%E2(Python编码问题的解决方案总结)
文本类型中有一种是json的格式,json模块中的json.loads()可以将这种格式的文本转换成字典或则列表什么的。
urllib.request.urlopen(url,date)
翻译编码:
import urllib.request
import urllib.parse
import json
xuyaofanyideneirong=input('需要翻译的内容:')
fangwenwangye='http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule'
date={}
date['i']=xuyaofanyideneirong
date['from']='AUTO'
date['to']='AUTO'
date['smartresult']='dict'
date['client']='fanyideskweb'
date['salt']='1542554407027'
date['sign']='13b4b3afa475edda17f39ac5aac4acc6'
date['doctype']='json'
date['version']='2.1'
date['keyfrom']='fanyi.web'
date['action']='FY_BY_REALTIME'
date['typoResult']='false'
fanyifangfa=urllib.parse.urlencode(date).encode('utf-8')
wangyefanyifangfafangwen=urllib.request.urlopen(fangwenwangye,fanyifangfa)
wangyefanyifangfaduqu=wangyefanyifangfafangwen.read().decode('utf-8')
zifuchuanzhuanweizidian=json.loads(wangyefanyifangfaduqu)
fanyijieguo=zifuchuanzhuanweizidian['translateResult']['tgt']
print('翻译结果为:%s'%fanyijieguo)
BigSmall
发表于 2018-11-19 16:45:19
本帖最后由 BigSmall 于 2018-12-7 21:36 编辑
56隐藏
一般网站掐掉不正常的网站访问,为了使我们的程序能够持续的干活,那么我们需要对代码进行隐藏,让它看成是正常的网站访问。
服务器检查访问地址是检查headers里面的user-agent,来判断你是来自代码还是来自浏览器。
urllib.request.Request(url,date=None,headers={});通过修改headers可以设置新的headers,headers必须是一个字典;两种方法,一种是直接设置一个字典,写入Request的参数,另外一种是Request.add_header()。
代码
import urllib.request
import urllib.parse
import json
import time
while True:
content=input('需要翻译的内容(break退出):')
if content=='break':
break
url='http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule'
head={}
head['User-Agent']='Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'
date={}
date['i']=content
date['from']='AUTO'
date['to']='AUTO'
date['smartresult']='dict'
date['client']='fanyideskweb'
date['salt']='1542554407027'
date['sign']='13b4b3afa475edda17f39ac5aac4acc6'
date['doctype']='json'
date['version']='2.1'
date['keyfrom']='fanyi.web'
date['action']='FY_BY_REALTIME'
date['typoResult']='false'
date=urllib.parse.urlencode(date).encode('utf-8')
req=urllib.request.Request(url,date,head) #不同于上一节的直接实例化打开,这里先实例化req,然后加head参数,只有Request可以加head参数。或者这句后面加req.add_header()
html=response.read().decode('utf-8')
target=json.loads(html)
target=target['translateResult']['tgt']
print('翻译结果为:%s'%target)
time.sleep(5)
修改了header就可以了吗?并不行,一个爬虫一秒钟下载上百张图片,对服务器的压力很大,所以服务器会对访问过多的地址设置验证码,阻止爬虫。对于这种限制,有两种策略:一是延迟提交的时间(例如上面的代码),另一种是使用代理,用很多个代理发起访问(具体分析如下)。
代理的步骤:
1,设置一个代理网站ip的字典
2,定制一个opener,当我们打开网页的时候就是默认opener在工作,opener可指定headers或者指定ip地址来访问
3,安装opener;安装之后就可以一直使用了
4,如果只有在特殊的时候才调用opener,那我们就用opener.open(url)函数来调用
BigSmall
发表于 2018-12-8 14:20:50
略微感觉有些迷茫啊,搞什么也搞不下去
没人指导,自己学太慢、太累
前几天搞了几天科研,搞不出来啊,过来放松几天吧
BigSmall
发表于 2018-12-8 14:23:13
57