day02 数据类型


本节设计内容范围:基础 ~ 数据类型

周末直播课的目标: 强化知识 + 实战案例 + 名企面试题 + 收集学员问题 + 答疑

今日概要:

  • C语言基本知识

    • 字符和字符串(数组)
    • 指针(内存地址)
    • 结构体(链表)
  • Python内存管理机制

  • git入门

  • 实战案例(7个)

  • 企业面试题

  • 学员答疑

1.聊聊C语言

1.1 字符和字符串

  • 字符,用一个字节来存储字符。

    char v1 = 'w'
    
  • 字符串,用字符数组构造出来的一个字符串。

    char v2[8] = {'w','u','p','e','i','q','i','\0'}  // v1 = "wupeiqi"
    
    char v3[] = "wupeiqi"
    

在C语言中字符串是由多个字符串构成的字符数组搞出来。

在C语言中创建字符数组时,内存地址已创建好了(并且是连续的)。

char v2[8] = {'w','u','p','e','i','q','i','\0'}

char v3[] = "wupeiqi"
假设:
    0x7ffee1b2b752
    0x7ffee1b2b753
    0x7ffee1b2b754
    0x7ffee1b2b755
    0x7ffee1b2b756
    0x7ffee1b2b757
    ...
    0x7ffee1b2b758

如果c语言中修改字符串:

  • wupeiqi -> wubeiqi,可以
  • wupeiqi -> wupeipeipeipeiqi,不支持直接字符串修改,而是新创建一份。

在Python中的字符串:

name = "alex"
data = name.upper()

v1 = "灿老师谢新雪"
v2 = v1.replace("新","旧的")

注意:Python中的字符串不是简单的像C语言存储。

1.2 指针

Python的语法中没有提供指针。

想要在Python中创建一个指针(不可以)。

Python中的很多东西都是基于C语言的指针实现。

int v1 = 666; 
int* v2 = &v1; // 指针

01

指针的好处节省内存的开销:

char v1[] = "我是武沛齐,我喜欢嫂子的包的饺子";

char *v2 = &v1[0];
char *v3 = &v1[0];
char *v4 = &v1[0];
char *v5 = &v1[0];

02

在Python中我会写:

v1 = "嫂子满身大汉"
v2 = v1
v3 = v1

03

v1 = "嫂子满身大汉"
v2 = v1
v3 = v1
v4 = "嫂子满身大汉"

# 理论上:在内存中再开辟一个空间去存储。
# 优化机制:重用这个字符串。(Python优化机制)
# 如果这个时候定义v4=嫂子满身大汉,是不是会另外开新的地址?
>>> v1 = 'wupeiqi'
>>> v2 = v1
>>>
>>>
>>> v3 = "alex"
>>> v4 = "alex"

>>> v5 = [11,22,33]
>>> v6 = [11,22,33]
>>> id(v5)
140231354254848
>>> id(v6)
140231354254912
>>>
>>> v7 = "alex"
>>> v8 = v7
>>> v7[0] = "A" # 不支持对字符串的元素进行修改。

>>> v9 = [11,22,33]
>>> v10 = v9
>>> v9[0] = 666

注意:重新赋值?还是修改内部元素。

v7 = "alex"
v8 = "ALEX"
v9 = v7.upper()

1.3 结构体

int age = 18;
char name[30] = "武沛齐";
struct Person{
    char name[10];
    int age
}

struct Person p1 = {"苍老师",29};
struct Person p2 = {"有阪老师",21};
struct Person p3 = {"大桥老师",23};
p1.name
p1.age

1.单向链表

04

struct Node{
    int data;
    struct Person *next; // 指针,存储内存地址
}

struct Node n3 = {33};
struct Node n2 = {22, &n3};
struct Node n1 = {11, &n2};

n1.data
n1.next.data
n1.next.next.data

2.双向链表

05

struct Person
{
    int data;
    struct Person *next;
    struct Person *prev;
};

struct Person p3 = { 33 };
struct Person p2 = { 22 };
struct Person p1 = { 11 };

p1.next = &p2;

p2.next = &p3;
p2.prev = &p1;

p3.prev = &p2;

3.双向环状链表

06

struct Person
{
    int data;
    struct Person *next;
    struct Person *prev;
};

struct Person p3 = { 33 };
struct Person p2 = { 22 };
struct Person p1 = { 11 };

p1.next = &p2;
p1.prev = &p3;

p2.next = &p3;
p2.prev = &p1;

p3.prev = &p2;
p3.next = &p1;

2.Python内存管理机制

引用计数器为主、标记清楚和分代回收为辅。

  • Python中的所有的东西最终都会根据底层C代码来实现。

    v1 = 0.9  # 浮点型 --> 结构体(4个值)    {1, 0.9, 上一个, 下一个 }
    v2= 1     # 整型  -->  结构体(4个值)   {1, 1, 上一个, 下一个 }
  • 环状双向链表 refchain,用于保存我们在代码中创建的所有值。

  • 创建一个值 07

  • 删除一个值

    age = 18
    name = "武沛齐"
    nickname = name
    
    del name      # name变量删除; 引用计数器obj_refcnt减 1
    del nickname  # name变量删除; 引用计数器obj_refcnt减 1   ===> 0,如果引用计数器=0则认为他没用,就是垃圾。
    
    # 垃圾回收
    #   1. 在环状链表中摘除。
    #   2. 在内存中去销毁(缓存不销毁),以后如果再用的话。
    

上述都是基于引用计数器实现的,如果Python仅用这种是不行的,因为:会存在循环引用导致有些数据永远无法销毁的情况。

v1 = [11,22,33] # [11,22,33,v2]
v2 = [33,44,55] # [11,22,33,v1]
v1.append(v2)
v2.append(v1)

# 两个列表数据内部的引用计数器都是 2
del v1
del v2
# 两个列表数据内部的引用计数器都是 1

===== 以后代码中再也无法使用v1和v2======

所以搞了一个标记清楚的技术。

  • 专门维护一个链表去存储 ”可变类型“( 列表、元组、字典、集合、自定义类等对象)。
  • 内部会监测是否出现了循环引用,如果出现循环应用就会让各自的引用计数器-1,再检查是否是0;垃圾。

08

为了提高扫描的效率,Python又搞了一个 分代回收的技术。

09

// 分代的C源码
#define NUM_GENERATIONS 3
struct gc_generation generations[NUM_GENERATIONS] = {
    /* PyGC_Head,                                    threshold,    count */
    {{(uintptr_t)_GEN_HEAD(0), (uintptr_t)_GEN_HEAD(0)},   700,        0}, // 0代
    {{(uintptr_t)_GEN_HEAD(1), (uintptr_t)_GEN_HEAD(1)},   10,         0}, // 1代
    {{(uintptr_t)_GEN_HEAD(2), (uintptr_t)_GEN_HEAD(2)},   10,         0}, // 2代
};

3. git

Git软件,对文件夹中的文件版本控制(本地)。

代码仓库,保存所有代码,例如:github[公有/私有]、gitee[公有/私有]、gitlab(自己公司)。

10

3.1 Git

3.2 本地版本控制

  1. 今日要管理的文件件

  2. 初始化

    git init
  3. 开发程序、写代码和修改代码....

  4. 让git帮助我们将文件夹下的所有文件生成一个版本

    git add .              # 将当前文件夹中所有变化(新增、修改内容、删除)暂存起来。【红色->绿色】
    git commit -m "日志"    # 生成一个提交记录
    第一次执行git commit 时,提示你要git config 配置(姓名&邮箱),在生成记录的时候要写下。
    git config ...

    12

  1. 继续开发、写代码和修改代码....

  2. 再生成一个版本

    git add .              # 将当前文件夹中所有变化(新增、修改内容、删除)暂存起来。【红色->绿色】
    git commit -m "日志"    # 生成一个提交记录
  3. 继续开发、写代码和修改代码....

  4. 再生成一个版本

    git add .              # 将当前文件夹中所有变化(新增、修改内容、删除)暂存起来。【红色->绿色】
    git commit -m "日志"    # 生成一个提交记录

如果说已经开发了10个版本,如果想要回滚到某个之前的版本。

  • 查看所有的版本记录

    git log
    commit 171ff4152a248d3ec49b52cdbe10129637cbd14a (HEAD -> master)
    Author: wupeiqi <wupeiqi@live.com>
    Date:   Sun Mar 14 14:10:56 2021 +0800
    
        开发了新功能直播
    
    commit 90db1d73717bce421e93fcd14f542bc93ee83f70
    Author: wupeiqi <wupeiqi@live.com>
    Date:   Sun Mar 14 14:08:13 2021 +0800
    
        初始化所有的文件
  • 回滚到指定版本

    git reset --hard 90db1d73717bce421e93fcd14f542bc93ee83f70
  • 又让我跳回到之前最新的版本。

    git reflog
    git reset --hard 版本号

3.3 代码仓库

建议:github(全球程序员集散地&交友平台)【以后写功能/找项目,去github找找】

建议:码云 gitee(开源中国)。

注意:千万不要把公司代码放上去(乌云)。

以码云为例:

  • 注册码云

  • 在码云:

    • 创建一个仓库(文件夹),一般与项目同名。 13 14

15

  • 在自己电脑:

    • 没创建项目

      创建项目文件夹
      进入项目的文件夹
      在终端执行 git init
      创建/删除/修改文件....
      git add . 
      git commit -m 'xxxxxx'
      
      git remote add origin https://gitee.com/wupeiqi/luffy666.git(写自己项目的仓库地址)
      git push origin master
    • 已经有的项目

      git remote add origin https://gitee.com/wupeiqi/luffy2.git       【只需要配置一次就行了】
      git push origin master    
      
      注意:git push origin master  是将本地代码推送到代码仓库。

如果想要继续开发代码。

16

现场作业:

  • 码云创建一个仓库
  • 本地随便创建项目
  • 本地项目推送到码云的残酷

通过上述学习你能了解的:

  • gitee创建仓库。
  • 本地git管理本地仓库 + 推到远程仓库。
  • 本地写代码。

3.4 第一天去公司

老板会告诉你去熟悉:https://gitee.com/bestpython/zack.git

通过命令去下载/获取别人的代码:

git clone https://gitee.com/bestpython/zack.git
cd zack
git pull origin master

继续开发代码

git add .
git commit -m 'xx'
git push origin master

建议

写新项目:

    1.用pycharm创建一个项目。  D:\proj\crm 【代码+requirements.txt】
    2.创建一个虚拟环境。F:\learn\envs\vcrm
    3.在python中配置 项目 和虚拟 环境关联。
    4.写功能...
    5.生成requirements.txt
        pip freeze > requirements.txt

    6.在码云创建仓库 crm
    7.本地代码提交到 crm 仓库
        cd D:\proj\crm  【代码+requirements.txt】
        git init 
        git add .
        git commit -m '上线'
        git remote add origin http://xxx..xxxxx.git
        git push origin master

    更新代码
        写代码。。。。
        git add .
        git commit -m '上线'
        git push origin master

    更新代码
        写代码。。。。
        git add .
        git commit -m '上线'
        git push origin master

拿到别人的项目,想要在自己电脑上运行:
    1. git clone http://xxx..xxxxx.git
    2. 创建虚拟环境 + 绑定项目
    3. 在虚拟环境中安装相关包
        pip install -r requirements.txt
    4. 运行....


    更新代码(协同开发的权限)
        写代码。。。。
        git add .
        git commit -m '上线'
        git push origin master

赠送

  • 虚拟环境和项目放一起。 17
  • 先别放一起。

4.项目实战案例

4.1 重试执行

场景:通过爬虫访问亚马逊并获取,为指定商品提交价格的所有商家信息。

需求:最多重试5次。

"""
pip install requests
pip install BeautifulSoup4
"""
import requests
from bs4 import BeautifulSoup

# ############################ 向亚马逊发送请求 ############################
res = requests.get(
    url="https://www.amazon.com/gp/aod/ajax/ref=aod_f_new?qty=1&asin={}&pc=dp&pageno=1&filters=%257B%2522all%2522%253Atrue%252C%2522new%2522%253Atrue%257D".format(
        "B07V2BBMK4"),
    headers={
        "User-Agent": 'Mozilla/5.0 (Windows; U; Windows NT 5.2) Gecko/2008070208 Firefox/3.0.1',
        "pragma": "no-cache",
        "upgrade-insecure-requests": "1",
        "cache-control": "no-cache",
        "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7",
        "accept-encoding": "gzip, deflate, br",
        "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
    }
)
res.close()

if res.status_code == 200:
    print("成功")
else:
    print("请求失败")

# ############################ res.text内容中提取商户和价格 ############################

soup = BeautifulSoup(res.text, 'html.parser')
tag = soup.find(attrs={'id': "aod-offer-list"})
tag_list = tag.find_all(attrs={'id': "aod-offer"})
for tag in tag_list:
    sold_by = tag.find(attrs={'id': "aod-offer-soldBy"}).find('a').text.strip()
    price = tag.find(attrs={'class': "a-offscreen"}).text.strip()
    message = "{}\t\t{}".format(price, sold_by)
    print(message)
import requests
from bs4 import BeautifulSoup


def download_page(retry=5):
    while retry > 0:
        res = requests.get(
            url="https://www.amazon.com/gp/aod/ajax/ref=aod_f_new?qty=1&asin={}&pc=dp&pageno=1&filters=%257B%2522all%2522%253Atrue%252C%2522new%2522%253Atrue%257D".format(
                "B07V2BBMK4"),
            headers={
                "User-Agent": 'Mozilla/5.0 (Windows; U; Windows NT 5.2) Gecko/2008070208 Firefox/3.0.1',
                "pragma": "no-cache",
                "upgrade-insecure-requests": "1",
                "cache-control": "no-cache",
                "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7",
                "accept-encoding": "gzip, deflate, br",
                "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
            }
        )
        res.close()
        if res.status_code == 200:
            return res
        retry -= 1


def run():
    response = download_page()
    if not response:
        print("页面下载失败")
        return
    soup = BeautifulSoup(response.text, 'html.parser')

    tag = soup.find(attrs={'id': "aod-offer-list"})
    tag_list = tag.find_all(attrs={'id': "aod-offer"})
    for tag in tag_list:
        sold_by = tag.find(attrs={'id': "aod-offer-soldBy"}).find('a').text.strip()
        price = tag.find(attrs={'class': "a-offscreen"}).text.strip()
        message = "{}\t\t{}".format(price, sold_by)
        print(message)


if __name__ == '__main__':
    run()

4.2 构造特定字典

项目背景:服务器资产采集。

18

需求:

  • 文件disk.txt

    Enclosure Device ID: 32
    Slot Number: 1
    Drive's postion: DiskGroup: 0, Span: 0, Arm: 0
    Enclosure position: 0
    Device Id: 0
    WWN: 5000C5007272C288
    Sequence Number: 2
    Media Error Count: 0
    Other Error Count: 0
    Predictive Failure Count: 0
    Last Predictive Failure Event Seq Number: 0
    PD Type: SAS
    Raw Size: 279.396 GB
    Non Coerced Size: 278.896 GB [0x22dcb25c Sectors]
    Coerced Size: 278.875 GB [0x22dc0000 Sectors]
    Firmware state: Online, Spun Up
    Device Firmware Level: LS08
    Shield Counter: 0
    Successful diagnostics completion on :  N/A
    SAS Address(0): 0x5000c5007272c289
    SAS Address(1): 0x0
    Connected Port Number: 0(path0) 
    Inquiry Data: SEAGATE ST300MM0006     LS08S0K2B5NV            
    FDE Enable: Disable
    Secured: Unsecured
    Locked: Unlocked
    Needs EKM Attention: No
    Foreign State: None
  • 读取文件获取数据并构造成如下格式

    info = {
        "slot":"1",
        "capacity":"279.396 GB",
        "model":"SEAGATE ST300MM0006     LS08S0K2B5NV",
        "pd_type":"SAS"
    }
    

19

4.3 构造图表

20

21

import time
import datetime
import collections

"""
date_dict = {
    '2021-03-14':[1615651200000.0, 0],
    '2021-03-13':[1615564800000.0, 0],
    '2021-03-12':[1615478400000.0, 0],
    '2021-03-11':[1615392000000.0, 0],
    '2021-03-10':[1615219200000.0, 0],
    ...
}
"""

# 构造固定时间格式数据(空)
today = datetime.datetime.now().date()
date_dict = collections.OrderedDict()
for i in range(0, 30):
    date = today - datetime.timedelta(days=i)
    date_dict[date.strftime("%Y-%m-%d")] = [time.mktime(date.timetuple()) * 1000, 0]

# 去数据库获取数据
queryset = [
    ['2021-03-14', 10],
    ['2021-03-13', 19],
    ['2021-03-12', 2],
    ['2021-03-11', 0],
    ['2021-03-10', 1],
]
for row in queryset:
    date_dict[row[0]][-1] = row[1]

# ####################### 结构 ####################
for item in date_dict.values():
    print(item)

4.4 循环删除问题

22

  • 对象存储
  • 网络请求
思路:
    - 在数据库中把它删除(网站数据存储到数据库)
    - 去腾讯租的地方删除文件

文件:
    - 在数据库中把它删除(网站数据存储到数据库)
    - 去腾讯租的地方删除文件
文件夹:
    - 找到要删除的所有文件
    - 再去在数据库中把它删除(网站数据存储到数据库)
    - 再去去腾讯租的地方删除文件
    类似于:删除文件夹必须先删除文件中中的所有文件,然后才能删除文件夹。

注意:我只是已文件和文件为例给他介绍。

# 开头:文件列表
key_list = []

# 要删除的文件夹
folder_list = ["片子", ] # ["片子","喵喵","小罗",嘻嘻嘻嘻嘻]

for folder in folder_list:
    # folder = 片子文件件
    # 进入 `片子文件夹`获取他下面的所有的文件和文件夹
    child_list = ...(project=request.tracer.project, parent=folder).order_by('-file_type')

    # 循环 `片子文件夹` 目录下的 文件和文件夹
    for child in child_list:
        # 文件夹
        if child.file_type == 2: 
            folder_list.append(child)
        # 文件
        else:
            key_list.append({"Key": child.key})

if key_list:
    # 结果:批量删除所有的文件
    delete_file_list(key_list)

4.5 仓库管理

背景:从入学讲git。

  • 组织,学生加入组织(创建n项目)。
    • 组织:老男孩Python全栈21期
      • 学生加入组织
    • 在组织中创建项目:
      • 老师,创建1个。
      • 每个学生,项目。
    • 布置作业 issues(发帖子)

懒惰,不去评论(助教要求)。

学技术之后,自己更省事。

通过接口API去实现统计分析一般去GitHub上找找没有在自己造轮子
import requests

def load_members(org, role):
    data_list = []
    page = 1
    while True:
        result = requests.get(
            url='https://gitee.com/api/v5/orgs/{org}/members'.format(org=org),
            data={
                'org': org,
                'page': page,
                'per_page': 100,
                'role': role
            }
        )
        members = result.json()
        data_list.extend(members)
        if len(members) < 100:
            break
        page += 1
    data = {item['login']: {'username': item['login'], 'nick_name': item['name'], 'repo_name': []} for item in data_list}
    return data

4.6 发短信

https://pythonav.com/wiki/detail/10/81/

  • 注册腾讯云(免费)

  • 开通云短信(个人认证)

    • 创建应用

    • 签名(自己去申请公众号)

    • 保留以上获取的信息:

      appid = 112142311  # 自己应用ID
      appkey = "8cc5b87123y423423412387930004"  # 自己应用Key
      sms_sign = "Python之路"
  • python发短信

    pip install qcloudsms_py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import ssl
    # ssl._create_default_https_context = ssl._create_unverified_context
    from qcloudsms_py import SmsMultiSender, SmsSingleSender
    from qcloudsms_py.httpclient import HTTPError
    def send_sms_single(phone_num, template_id, template_param_list):
        """
        单条发送短信
        :param phone_num: 手机号
        :param template_id: 腾讯云短信模板ID
        :param template_param_list: 短信模板所需参数列表,例如:【验证码:{1},描述:{2}】,则传递参数 [888,666]按顺序去格式化模板
        :return:
        """
        appid = 112142311  # 自己应用ID
        appkey = "8cc5b87123y423423412387930004"  # 自己应用Key
        sms_sign = "Python之路"  # 自己腾讯云创建签名时填写的签名内容(使用公众号的话这个值一般是公众号全称或简称)
        sender = SmsSingleSender(appid, appkey)
        try:
            response = sender.send_with_param(86, phone_num, template_id, template_param_list, sign=sms_sign)
        except HTTPError as e:
            response = {'result': 1000, 'errmsg': "网络异常发送失败"}
        return response
    def send_sms_multi(phone_num_list, template_id, param_list):
        """
        批量发送短信
        :param phone_num_list:手机号列表
        :param template_id:腾讯云短信模板ID
        :param param_list:短信模板所需参数列表,例如:【验证码:{1},描述:{2}】,则传递参数 [888,666]按顺序去格式化模板
        :return:
        """
        appid = 112142311
        appkey = "8cc5b87123y423423412387930004"
        sms_sign = "Python之路"
        sender = SmsMultiSender(appid, appkey)
        try:
            response = sender.send_with_param(86, phone_num_list, template_id, param_list, sign=sms_sign)
        except HTTPError as e:
            response = {'result': 1000, 'errmsg': "网络异常发送失败"}
        return response
    if __name__ == '__main__':
        result1 = send_sms_single("15131255089", 548760, [666, ])
        print(result1)
        result2 = send_sms_single( ["15131255089", "15131255089", "15131255089", ],548760, [999, ])
        print(result2)