python高级

Posted by 琼脂糖 on September 18, 2019

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. 继承

  1. class Mammal(Animal)
  2. isinstance issubclass
  3. method overriding: super().__init__()
    如果没有super语句,那么子类的init就彻底覆盖父类的init噢
class Mammal(Animal):
    def __init__(self):
        print("Mammal Constructor")
        self.weight = 2
        super().__init__()
  1. 多层继承:最好一层到两层。太多会造成代码混乱

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

  1. iterdir
    # 返回path下面的所有目录 生成paths列表
    from pathlib import Path
    paths = [p for p in path.iterdir() if p.is_dir()]
    
    • iterdir()返回的是一个生成器
    • 缺点:不能search pattern ;不能search recursively
  2. glob :search pattern
    py_files = [p for p in path.glob("*.py")]
  3. 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)

  1. 不是所有app或网站都有api可以获取数据。这时候就要用爬虫来parse HTTP网页,获取所需要的数据。
  2. 安装包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

  1. 包 pypdf2

11.4. excel

  1. 包openpyxl
  2. 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")