Python学习笔记。包括exception,模块,标准库,pypi等。
6. Exception
Exception的作用是:prevent program from crashing
try except else结构:else- 当try的代码没有throw exception的时候执行
try:
age = int(input("Age: "))
except ValueError:
print("You didn't enter a valid age.")
else:
print("No exceptions were thrown.")
print("Execution continues.")
except ValuesError as ex:
ex为error message
except (ValueError, ZeroDivisionError):
多个exception
finaly:无论有没有exception都可以执行
try:
file = open("app.py")
age = int(input("Age:"))
xfactor = 10 / age
except (ValueError, ZeroDivisionError):
print("You didn't enter a valid age.")
else:
print("No exceptions were thrown.")
finally:
file.close()
with
当一个对象有enter和exit magic method,那么这个obj支持context management protocol。就可以用with。Python会自动call exit method,然后释放内存
with open("app.py") as file:
print("File opened.")
可以定义自己的exception:
首先python built-in exception已经很充足了,但也可以定义自己的
def calculate_xfactor(age):
if age <= 0:
raise ValueError("Age cannot be 0 or less.")
return 10 / age
try:
calculate_xfactor(-1)
except ValueError as error:
print(error)
raise exception很耗资源
可以return none:
if age <= 0:
return None
运行时间
from timeit import timeit
code1=””” “””
timeit(code1, number=100000)
7. 类
7.1. 基础
Class: blueprint for creating new objects Object: instance of a class
比如 Class: Human Objects: John, Mary, Jack
isinstance(obj,class)返回布尔值
7.2. init 魔法方法
是新的实类创建的时候自动调用的方法,也称为constructor。一般用来将参数初始化
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def draw(self):
print(f"({self.x}, {self.y})")
point = Point(1, 2)
point.draw()
7.3. 实类和类属性&方法(instance & class attribute & method)
instance attribute不是一定要在init中声明的。在class外面也可以声明。只需要把在class内用到的属性在init中声明。
point = Point(1, 2)
point.z = 4
point.draw()
print(point.x, point.y, point.z)
instance att属于每一个instance,不同的instance可以有不同的att。
class att可以通过class或者instance来访问。
claas att被所有instance共享。如果改变其值,所有instance都同时生效。
class Point:
default_color = "red"
class method:可用来批量建instances。也称为factory方法。
比如创建一个x和y都是0的点。
point = Point(0, 0) # constructor
another_point = Point.zero() # factory(class method)
@classmethod
def zero(cls):
return cls(0, 0)
需要decorator。
7.4. 魔法方法
根据我们使用obj的方式来自动的调用某个方法。
str方法:当我们想把obj转换成string的时候调用。默认返回名称和内存位置。
def __str__(self):
return f"({self.x}, {self.y})"
比较 eq gt
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __gt__(self, other):
return self.x > other.x and self.y > other.y
7.5. 自定义容器
容器:dict list set
自定义意义:可以大小写不敏感
class TagCloud:
def __init__(self):
self.__tags = {}
# 1.add 添加item到字典
def add(self, tag):
self.__tags[tag.lower()] = self.__tags.get(tag.lower(), 0) + 1
#cloud["python"]来得到其count
def __getitem__(self, tag):
return self.__tags[tag.lower()]
#cloud["python"] = 10
def __setitem__(self, tag, count):
self.__tags[tag.lower()] = count
#for tag in cloud(iterable)
def __iter__(self):
return iter(self.__tags)
cloud = TagCloud()
cloud.add("Python")
cloud.add("Python")
cloud.add("python")
cloud.add("PYTHON")
print(cloud.__tags["python"])
私有属性:cloud[“python”]相当于cloud.tags[“python”]
但后者是大小写敏感的。需要将tags作为私有属性,不能从外面访问
选中tags,按F2,用来集中修改属性名字,需要装个包。加两个下划线
通过__dict__可以访问私有属性
7.6. property
考虑:product(-50) 这样不能赋值给price,而应该返回错误。
在set_price函数中来赋值。另外不希望price从外面访问,因此作为私有属性。点+price的是属性,改为下划线price。
price是从外传入的变量,不一样哈。
class Product:
def __init__(self, price):
self.set_price(price)
def get_price(self):
return self.__price
def set_price(self, price):
if price < 0:
raise ValueError
self.__price = price
product = Product(-50)
property:an obj sits in front of an attri, and allow us to get or set the value of an attr.
在get和set之后,定义一个class attri。
property(fget,fset,fdel,doc) :获得属性值的函数,设定属性值的函数
property从外面看就像一个regular attribute。内部是两个method:getter和setter
隐藏getter和setter:
方法一:名字前面加下划线 (但不推荐,extra noise)
推荐方法decorator
@property: 自动创建一个property obj
@price.setter
class Product:
def __init__(self, price):
self.price = price
@property
def price(self):
return self.__price
@price.setter
def price(self, price):
if price < 0:
raise ValueError("price cannot be less than 0")
self.__price = price
product = Product(-5)
print(product.price)
如果没有setter,那么property是read-only的,初始化给了参数后,后面不能修改
property作用:1)属性实际是通过getter访问的,setter设定的(可以有一定限制条件)。但写法仍然是点访问。2)setter不写的话就是只读的
7.6. 继承
- class Mammal(Animal)
- isinstance issubclass
- method overriding:
super().__init__()
如果没有super语句,那么子类的init就彻底覆盖父类的init噢
class Mammal(Animal):
def __init__(self):
print("Mammal Constructor")
self.weight = 2
super().__init__()
- 多层继承:最好一层到两层。太多会造成代码混乱
multiple继承:
class Manager(Person,Employee)
谨慎使用:当几个类都是小类而且没有没有共同点可以使用。
class Flyer:
def fly(self):
pass
class Swimmer:
def swim(self):
pass
class FlyingFish(Flyer, Swimmer):
pass
一个好的例子
class InvalidOperationError(Exception):
pass
class Stream:
def __init__(self):
self.opened = False
def open(self):
if self.opened:
raise InvalidOperationError("alreay open")
self.opened = True
def close(self):
if not self.opened:
raise InvalidOperationError("already closed")
self.opened = False
class FileStream(Stream):
def read(self):
print("read from a file")
class NetworkStream(Stream):
def read(self):
print("read from a network")
7.7. abstract base class
code
from abc import ABC, abstractmethod
class AbstractClassExample(ABC):
@abstractmethod
def do_something(self):
print("Some implementation!")
class AnotherSubclass(AbstractClassExample):
def do_something(self):
super().do_something()
print("The enrichment from AnotherSubclass")
x = AnotherSubclass()
x.do_something()
在子类中,do_something必须要有,否则会报错。用法相当于一个约定,或模版。
7.9. namedtuple
如果类里面只有属性,没有method。也就是 data class,可以用namedtuple来代替。
如果要修改属性,只能重新建立一个新的point
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
p1 = Point(x=1, y=2)
# p1 = Point(x=10, y=2)
p2 = Point(x=1, y=2)
print(p1 == p2)
8. Module
创建module
将功能相近的属性和method放在一个app.py文件中。
调用:from app import … 按ctrl+space来选择
from app import * : 不好,可能同名覆盖
import app : 然后app.func来用函数和属性
path
```import sys
print(sys.path)
导入子文件夹ecommerce的module:在文件夹下新建 __init__.py,这样python会将这个文件夹作为package
```python
from ecommercy.app import func1
##or
import ecommery.app
dir(app) 返回app module里面的属性和method
模块的内置属性__name__,__file__,__package__
:
ecommerce.shopping.sales
ecommer.shopping
/Users/mosh/Courses/Python/Demo/HelloWorld/ecommerce/shopping/sales.py
The name of the module that starts the program is main.
比如从a脚本调用b模块,那么b中的__name__
属性就是b的名字。
直接运行b脚本,那么b中的name属性就是__main__
if __name__ == "__main__":
print("Sales started")
calc_tax()
如果直接运行脚本,就执行if中的内容;如果脚本作为模块导入其他脚本,就不执行if中的内容。
9. 标准库
9.1. path
from pathlib import Path
Path(r"C:\Program Files\Microsoft")
Path("/usr/local/bin")
Path()
Path("ecommerce/__init__.py")
Path() / Path("ecommerce")
Path() / "ecommerce" / "__init__.py"
Path.home()
path = Path("ecommerce/__init__.py")
path.exists()
path.is_file()
path.is_dir()
print(path.name) #__init__.py
print(path.stem) #__init__
print(path.suffix) #.py
print(path.parent) #ecommerce
path = path.with_name("file.txt") #ecommerce/file.txt
path.absolute() #绝对路径
path = path.with_suffix(".txt") #ecommerce/__init__.txt
path.exists()
path.mkdir()
path.rmdir()
path.rename("ecommerce2")
9.2. directory
- iterdir
# 返回path下面的所有目录 生成paths列表 from pathlib import Path paths = [p for p in path.iterdir() if p.is_dir()]
- iterdir()返回的是一个生成器
- 缺点:不能search pattern ;不能search recursively
- glob :search pattern
py_files = [p for p in path.glob("*.py")]
- rglob:search recursively
py_files = [p for p in path.glob("**/*.py")] py_files = [p for p in path.rglob("*.py")]
9.3. file
path.rename("init.txt")
path.unlink() #delete
path.stat() #统计信息
from time import ctime
ctime(path.stat().st_ctime) #ctime转换成尅看懂的时间信息 创建时间
读取和写入 (可以自行close)
path = Path() / "app.py"
print(path.read_text())
#same
with open(file,"r") as file: ..
path还有read_bytes方法,针对非文本文件。
复制file(shutils)
source = Path("ecommerce/__init__.py")
target = Path() / "__init__.py"
#方法一:冗杂代码
target.write_text(source.read_text())
#方法二:推荐
import shutil
shutil.copy(source, target)
9.4. zip
from pathlib import Path
from zipfile import ZipFile
#将文件夹打包
with ZipFile("files.zip", "w") as zip:
for path in Path("ecommerce").rglob("*.*"):
zip.write(path)
#read zipfile
with ZipFile("files.zip") as zip:
print(zip.namelist()) # 打印zip中的所有文件
info = zip.getinfo("ecommerce/__init__.py")
print(info.file_size)
print(info.compress_size)
zip.extractall("extract") # 解压到extract文件夹
```
## 9.5. csv
```python
import csv
with open("data.csv", "w") as file:
writer = csv.writer(file)
writer.writerow(["name", "age", "price"])
writer.writerow([1000, 1, 5])
writer.writerow([1001, 2, 15])
with open("data.csv") as file:
reader = csv.reader(file)
for row in reader:
print(row)
9.6. random
import string
import random
print(random.random())
print(random.randint(1, 10))
print(random.choice([1, 2, 3, 4])) # 从列表中随机选一个
print(random.choices([1, 2, 3, 4], k=2)) # 随机选k个
#password
print("".join(
random.choices(
string.ascii_letters + string.digits, k=4)
))
#打乱
numbers = [1, 2, 3, 4]
random.shuffle(numbers)
print(numbers)
9.7. 打开浏览器
import webbrowser
print("Completed.")
webbrowser.open("http://google.com")
9.8. mail
完整code(网上)
import smtplib
from email.header import Header
from email.mime.text import MIMEText
#第三方 SMTP 服务
mail_host = "smtp.163.com" # SMTP服务器
mail_user = "chashuguzi" # 用户名
mail_pass = "snowwhite1723" # 授权密码,非登录密码
sender = 'chashuguzi@163.com' # 发件人邮箱(最好写全, 不然会失败)
receivers = ['freyasnow@163.com'] # 接收邮件,可设置为你的QQ邮箱或者其他邮箱
content = '我用Python'
title = '人生苦短' # 邮件主题
def sendEmail():
message = MIMEText(content, 'plain', 'utf-8') # 内容, 格式, 编码
message['From'] = "{}".format(sender)
message['To'] = ",".join(receivers)
message['Subject'] = title
try:
smtpObj = smtplib.SMTP_SSL(mail_host, 465) # 启用SSL发信, 端口一般是465
smtpObj.login(mail_user, mail_pass) # 登录验证
smtpObj.sendmail(sender, receivers, message.as_string()) # 发送
print("mail has been send successfully.")
except smtplib.SMTPException as e:
print(e)
if __name__ == '__main__':
sendEmail()
9.9. command line
import sys
if len(sys.argv) == 1:
print("USAGE: python3 app.py <password>")
else:
password = sys.argv[1]
print("Password", password)
9.9. External programs
import subprocess
completed = subprocess.run(["ls", "-l"])
print("args", completed.args)
print("returncode", completed.returncode)
print("stderr", completed.stderr)
print("stdout", completed.stdout)
args [‘ls’, ‘-l’] returncode 0 stderr None stdout None
returncode 0 表示成功。
stdout 如果没有capture是没有的。如果需要capture,可以参数capture_output = TRUE,text设为true将binary转换为text
completed = subprocess.run(["ls", "-l"],capture_output=True,text=True)
run child process
completed = subprocess.run(["python3", "other.py"],
capture_output=True,
text=True)
从一个python脚本中调用另一个python脚本
check 设为true,如果有错误,会raise exception
import subprocess
try:
completed = subprocess.run(
["false"],
capture_output=True,
text=True,
check=True)
except subprocess.CalledProcessError as ex:
print(ex)
10. pypi
10.1. pypi
python package index:大家建立的第三方库
https://pypi.org
10.2. pip
我们通过pip来安装pypi
python3 相对应用pip3
python3.7 对应pip3.7
pip3 install requests #安装requests包
pip3 install --upgrade pip #更新pip
pip3 list #当前安装pypi
pip3 install requests==2.9.0
pip3 install requests==2.9.* #该版本下的最新版
pip3 uninstall requests #卸载
pip3 install requests ~=2.9.0 #latest compatible version
在pypi.org网站requests包里查看release history.
包和python自带module一样使用 import requests
import requests
response = requests.get("http://www.baidu.com")
print(response)
10.3. 虚拟环境
一般做法: 在需要的文件夹,建立一个叫env的虚拟环境
python3.7 -m venv env
会创建env文件夹,里面有python 解译器及包。在lib/python3.7/site-packages下面就是我们需要安装不同版本包的地方。
启动虚拟环境:source env/bin/activate.bat
退出虚拟环境:deactivate
(env)HelloWorld $ 在该环境下安装需要的包。
推荐做法:pipenv
pip3 install pipenv
pipenv install requests #自动建立虚拟环境并安装requests
创建了pipfile和pipfile.lock
虚拟环境地址 pipenv --venv
进入虚拟环境pipenv shell
退出exit
包安装位置:全局或者虚拟环境
vscode的code runner python interpreter是全局python,而不是虚拟环境中的python intepreter
如何让code runner用虚拟环境中python interpreter:
pipenv --venv
找到python位置,open
可以打开文件夹查看
将文件夹/bin/python3路径 设置到vscode的设置中,方法同最初设置python3
如何让vscode用虚拟环境中的python interpreter:
左下角的python版本改为虚拟环境中的python,如果找不到,在设置中加上下面,引号内写python的路径
“python.pythonPath”: “”,
一个项目有虚拟环境的话,其文件夹中就有pipfile和pipfilelock。但相关的所有包不是装在该文件夹下,位置在--env
可以查到
如果需要在另一台电脑上运行,可以根据pipfile或者lock文件重建虚拟环境所有的包
pipfile
- 包的版本是根据安装的一样。如果没有指定,那么这里也不会指定,写为星号
- 根据pipfile安装所有包
pipenv install
pipfile.lock: - 严格记录所有包及依赖包目前的版本
- 忽略pipfile,根据lock中的版本来安装
pipenv install --ignore-pipfile
11. popular python packages
11.1. Yelp API
Yelp是一个点评网站,api即application program interface。可以获取app的数据。
yelp fusion界面创建app,得到ID和KEY。
buisiness endpoint中的buisiniess search找到GET url。
新建项目:文件夹PyYelp,新建虚拟环境并安装requests。将左下角python编辑器改为虚拟环境的python,并在termial而不是coderunner中运行程序。
pip3.7 install pipenv
cd PyYelp
pipenv install requests
错误401:authentication erro。需要api key。
错误400:bad requests
print respnse.text
查看server返回的信息。有必须提供的参数。
正确返回,response.text返回一个json obj
response.json()将结果转换成dictionary
code
import requests
url = "https://api.yelp.com/v3/businesses/search"
api_key = "Ud6gzFpKeMrn7V7w8kWdVpetiZEUAj7X1lOfb3CmhQK9I_5WeMw5dsT5twYy9Z6poL3H8Jeo-WIcAnr2eOYhw2EtapoWrizhl-cb304Gnx-tbuDObmqSbJYF-husXHYx"
headers = {
"Authorization": "Bearer " + api_key
}
params = {
"location": "NYC",
"term": "barber"
}
response = requests.get(url, headers=headers, params=params)
businesses = response.json()["businesses"]
#for business in businesses:
#print(business["name"])
#get name of rate > 4.5
names = [business["name"]
for business in businesses if business["rating"] ≥ 4.5]
print(names)
git中如何隐藏api key
将api key 存在独立的文件config.py中,然后在app.py中import config
,调用使用config.api_key
新建一个文件.gitignore,在这个文件中写config.py
。 git就不会包含这个文件。
11.2. 网络爬虫(web crawler/spider)
- 不是所有app或网站都有api可以获取数据。这时候就要用爬虫来parse HTTP网页,获取所需要的数据。
- 安装包beautifulsoup4
response.text 返回网页的html代码
soup.select参数css选择器, .question
为类选择器
比如得到的div obj,其属性存在obj.attr的dictionary中
questions = soup.select(".question-summary")
print(questions[0].attrs)
可中括号读取属性值
print(questions[0]["id"])
用get 可以防止属性值为空。
print(questions[0].get("id", 0))
select_one 针对单一元素,只选择一个。比如每个summary中只有一个标题。否则会搜索整个div,并返回一个列表。
import requests
from bs4 import BeautifulSoup
response = requests.get("https://www.stackoverflow.com/questions/")
soup = BeautifulSoup(response.text, "html.parser")
questions = soup.select(".question-summary")
for question in questions:
print(question.select_one(".question-hyperlink").getText())
print(question.select_one(".vote-count-post").getText())
11.3. pdf
- 包 pypdf2
11.4. excel
- 包openpyxl
- workbook
wb = openpyxl.Workbook
:create a new workbook
wb = openpyxl.load_workbook("transactions.xlsx")
:打开workbook
wb.sheetnames
:属性sheetnames 返回sheet名字
sheet
sheet = wb['Sheet1']
wb.create_sheet("Sheet2", 0)
: 创建sheet并放在最前面
wb.remove_sheet(sheet)
cell
cell = sheet["a1"]
cell = sheet.cell(row=1, column=1)
print(cell.value) : transaction_id
print(cell.row)
: 1
print(cell.column)
: 1
print(cell.coordinate)
: A1
得到所有的值
for row in range(1, sheet.max_row + 1):
for column in range(1, sheet.max_column + 1):
cell = sheet.cell(row, column)
print(cell.value)
获取值
column = sheet["a"]
print(column)
cells = sheet["a:c"]
print(cells)
inner tuple :每column
print(sheet["a1:c3"])
print(sheet[1:3])
save
sheet.append([1, 2, 3])
wb.save("transcations2.xlsx")