0基础跟德姆(dom)一起学AI Python基础10-PyMySQL-解决SQL注入攻击,递归

* PyMySQL-SQL注入攻击问题
* PyMysql-操作事务
* 递归相关概述
* 递归相关案例
  * 入门案例
  * 求阶乘
  * 斐波那契数列(不死神兔)
  * 爬楼梯
  * 打印文件夹(及其子级)
  * 删除文件夹
  * 拷贝文件夹

---
1.PyMySQL-演示SQL注入攻击

* SQL注入攻击介绍

  * 概述

    > 实际开发中, 如果我们的SQL语句中 部分内容是由用户键盘录入的, 此时如果用户录入一些非法值, 就很有可能改变了我们SQL语句的结构, 从而引发一系列的安全问题, 这些安全问题就是: SQL注入攻击.

  * 例如: 模拟登陆

    ```sql
    -- 模拟登陆, 语法格式如下
    select * from users where username='用户录入的账号' and password='用户录入的密码';
    
    -- 可能发生的情况
    -- 场景1: 小白用户. 录入的账号: abc  录入的密码: xyz
    select * from users where username='abc' and password='xyz';     -- 合法的SQL
    
    -- 场景2: 懂点技术的用户. 录入的账号: abc  录入的密码: xyz ' or '1=1
    select * from users where username='abc' and password='xyz ' or '1=1';
    ```

* Python代码-演示SQL注入攻击问题

  * SQL代码-准备动作.

    ```sql
    -- ------------------------- 我是华丽的分割线 -----------------------
    -- 1. 建库, 切库, 查表.
    create database if not exists day10;
    use day10;
    show tables;
    
    -- 2. 建表, 添加表数据, 并查看数据.
    create table users(
        id int primary key auto_increment, # id, 主键自增
        username varchar(20),              # 账号(用户名)
        password varchar(20)               # 密码
    );
    insert into users values(null, 'admin01', 'pwd111'), (null, 'admin02', 'pwd222');
    select * from users;
    
    -- 场景1: 小白用户. 录入的账号: abc  录入的密码: xyz
    select * from users where username='abc' and password='xyz';     -- 合法的SQL
    
    -- 场景2: 懂点技术的用户. 录入的账号: abc  录入的密码: xyz ' or '1=1
    select * from users where username='abc' and password='xyz ' or '1=1';
    ```

  * Python代码

    ```python
    """
    SQL注入攻击介绍:
        概述:
            实际开发中, 如果我们的SQL语句中 部分内容是由用户键盘录入的, 此时如果用户录入一些非法值,
            就很有可能改变了我们SQL语句的结构, 从而引发一系列的安全问题, 这些安全问题就是: SQL注入攻击.
        例如: 模拟登陆
            -- 模拟登陆, 语法格式如下
            select * from users where username='用户录入的账号' and password='用户录入的密码';
    
            -- 可能发生的情况
            -- 场景1: 小白用户. 录入的账号: abc  录入的密码: xyz
            select * from users where username='abc' and password='xyz';     -- 合法的SQL
    
            -- 场景2: 懂点技术的用户. 录入的账号: abc  录入的密码: xyz ' or '1=1
            select * from users where username='abc' and password='xyz ' or '1=1';
    """
    
    # 导包
    import pymysql
    
    # 1. 提示用户录入他/她的账号和密码, 并接收.
    uname = input('请录入您的账号: ')
    pwd = input('请录入您的密码: ')
    
    # 2. 创建连接对象.
    conn = pymysql.connect(host='localhost', port=3306, user='root', password='123456', database='day10', charset='utf8')
    # 3. 创建游标对象.
    cursor = conn.cursor()
    # 4. 拼接 模拟登陆的 SQL语句.
    # 写法1: 普通写法, 直接拼接即可.
    # sql = "select * from users where username='"+ uname +"' and password='"+ pwd +"';"
    # 写法2: 占位符思想
    # sql = "select * from users where username='%s' and password='%s';" % (uname, pwd)
    # 写法3: 格式化思想, 插值表达式.
    sql = f"select * from users where username='{uname}' and password='{pwd}';"
    # print(sql)
    
    # 5. 执行(查询)SQL语句, 获取结果集.
    cursor.execute(sql)
    result = cursor.fetchall()
    # 6. 处理(操作)结果集. 即: 结果集有数据就提示登录成功, 无数据则提示登录失败.
    # 方式1: 根据 结果集的长度 判断.
    # if.else 语句写法
    # if len(result) > 0:
    #     print(f'登陆成功, 欢迎您, {uname}!')
    # else:
    #     print('登陆失败!')
    # 三元表达式写法.
    # print(f'登陆成功, 欢迎您, {uname}!' if len(result) > 0 else '登陆失败!')
    
    # 方式2: 根据 结果集的内容 判断.
    print(f'登陆成功, 欢迎您, {uname}!' if result else '登陆失败!')
    
    # 7. 释放资源.
    cursor.close()
    conn.close()
    ```

2.PyMySQL-解决SQL注入攻击

* 思路

  采用**预编译思想(占位符)**来解决, 即: 预先对SQL语句进行编译, 之后无论传入什么, 都只会当做普通字符来处理. 

* 示例代码

  ```python
  """
  PyMysql解决SQL注入攻击的思想:
      采用预编译思想(占位符)来解决, 即: 预先对SQL语句进行编译, 之后无论传入什么, 都只会当做普通字符来处理.
  """
  
  # 导包
  import pymysql
  
  # 1. 提示用户录入他/她的账号和密码, 并接收.
  uname = input('请录入您的账号: ')
  pwd = input('请录入您的密码: ')
  
  # 2. 创建连接对象.
  conn = pymysql.connect(host='localhost', port=3306, user='root', password='123456', database='day10', charset='utf8')
  # 3. 创建游标对象.
  cursor = conn.cursor()
  
  # 4. 拼接 模拟登陆的 SQL语句.
  # 预编译思想 结合占位符, 即: 这一步已经把SQL语句的格式给确定了(预编译过了), 之后无论传入什么, 都只会当做普通字符来处理.
  # sql = "select * from users where username='%s' and password='%s';" % (uname, pwd)   # 这种是 普通的 占位符思想, 没有预编译, 不会解决SQL注入攻击问题.
  
  # 预编译的时候, 用 %s 来占位, 在执行SQL语句的时候, 可以给这些占位符传值..
  sql = "select * from users where username=%s and password=%s;" # 这种是 预编译思想 + 占位符, 能解决SQL注入攻击问题.
  # print(sql)
  
  # 5. 执行(查询)SQL语句, 获取结果集.
  cursor.execute(sql, (uname, pwd))   # 在执行SQL语句的时候, 用容器类型(列表, 元组等)给 预编译时的占位符传值.
  result = cursor.fetchall()
  # 6. 处理(操作)结果集. 即: 结果集有数据就提示登录成功, 无数据则提示登录失败.
  # 方式1: 根据 结果集的长度 判断.
  # print(f'登陆成功, 欢迎您, {uname}!' if len(result) > 0 else '登陆失败!')
  
  # 方式2: 根据 结果集的内容 判断.
  print(f'登陆成功, 欢迎您, {uname}!' if result else '登陆失败!')
  
  # 7. 释放资源.
  cursor.close()
  conn.close()
  ```

3.PyMysql-操作事务

* 事务介绍

  * 概述

    > 事务**(transaction)**指的是逻辑上的一组操作, 组成该操作的各个逻辑单元, 要么全部执行成功, 要么全部执行失败.
    >
    > 大白话记忆: 同生同死.

  * 例如: 转账,   乔峰 => 虚竹 转 1000 元

    * 动作1: 乔峰账号 - 1000元

    * 动作2: 虚竹账号 + 1000元

      > 上述的两个动作 = **2个逻辑单元**,   合起来, 就是完整的 转账动作. 

  * 特点: (ACID)

    * 原子性

      > 指的是: 组成事务的各个逻辑单元已经是最小单位, 不可分割. 

    * 一致性

      > 指的是: 事务执行前后, 数据应该保持一致. 

    * 隔离性(Isolation)

      > 指的是: 事务之间是相互隔离的, 一个事务执行期间不应该受到其它事务的干扰. 
      >
      > 否则容易出现: 脏读, 不可重复读, 虚读(幻读)等情况...
      >
      > ```sql
      > -- 其实考虑隔离性, 就是设置不同的隔离级别, 具体如下:
      > -- 隔离级别, 安全性, 从低到高分别是:
      > read uncommitted < read committed < repeatable-read < serializable
      > 
      > -- 效率, 从高到低分别是:
      > read uncommitted > read committed > repeatable-read > serializable
      > 
      > -- MySQL数据库的默认隔离级别是: repeatable-read
      > -- Oracle数据库的默认隔离级别是: read committed
      > 
      > -- 四个隔离级别解释
      > read uncommitted: 
      >     会发生: 脏读, 不可重复读, 虚读
      >     已解决: 0
      >     
      > read committed: 
      >     会发生: 不可重复读, 虚读
      >     已解决: 脏读
      >     
      > repeatable-read: 
      >     会发生: 虚读
      >     已解决: 脏读, 不可重复读
      >     
      > serializable: 
      >     会发生: 0
      >     已解决: 脏读, 不可重复读, 虚读
      > ```

    * 持久性

      > 指的是: 无论事务执行成功与否, 结果都会永久的写到数据表中.

  * 涉及到的SQL语句

    ```sql
    -- MySQL默认开启了事务的自动提交功能, 每个SQL语句都是一个单独的事务, 执行完都会自动提交.
    -- 查看事务的自动提交功能.
    show variables like '%commit%';
    
    -- 查看事务的隔离级别
    select @@transaction_isolation;        -- 这个是: MySQL8.X的写法.
    select @@tx_isolation;                -- 这个是: MySQL5.X的写法.        
    
    -- 提交事务
    commit        -- 相当于: 把事务的执行结果给永久保存了.
    
    -- 事务回滚
    rollback    -- 相当于: 撤销本次执行, 即: 把数据还原到事务没有开始执行前的状态.
    
    -- 开启事务
    begin  或者写 start transaction
    
    -- 临时的设置事务的隔离级别为指定级别, 会话结束, 本次设置失效.
    set session transaction isolation level 隔离级别; 
    ```

* 扩展-演示脏读

  ```sql
  -- 脏读: 也叫 读-未提交, 指的是1个事务读取到了另1个事务还没有提交的数据, 导致查询结果不一致.
  具体动作:
      1. 开启两个DOS窗口, 称之为: A, B窗口.
      2. 设置A窗口的隔离级别为: read uncommitted(可能发生: 脏读, 不可重读, 虚读)
         B窗口的隔离级别不变: 默认还是 repeatable-read
      3. 在A, B窗口中分别开启事务.
      4. 在A窗口中先查询一下 账务表(account)的数据. 
      5. 在B窗口中完成 乔峰 => 虚竹 转账1000的动作, 注意: 不要提交.
      6. 在A窗口中再次查询一下 账务表(account)的数据, 发现和刚才查询的结果: 不一样.
      7. 此时出现了: A窗口 读取到了 B窗口 未提交的数据, 这就是 => 脏读.
  
  -- 不可重复读: 也叫 读-已提交(修改)
  
  -- 虚读: 也叫 读-已提交(插入)
  
  -- 串行化读法 => Serializable
  ```

* Python代码-模拟转账-**无事务**

  ```python
  """
  需求: PyMysql演示 转账, 无事务版, 看看会发生什么问题.
  
  遇到的问题:
      账号A 扣钱 成功, 但是 账号B 收钱 失败.
  产生原因:
      没有把 账号A扣钱 和 账号B收钱 这两个操作放在一起, 所以它们是两个独立的动作, 各不相关.
  解决方案:
      把 账号A扣钱 和 账号B收钱 这两个动作绑定到一起, 要么全部成功, 要么全部失败.
      这就可以通过 事务 来实现.
  """
  
  # 导包
  import pymysql
  
  # 1. 获取连接对象.
  conn = pymysql.connect(host='localhost', port=3306, user='root', password='123456', database='day10', charset='utf8')
  # 2. 获取游标对象.
  cursor = conn.cursor()
  # 3. 执行SQL语句, 获取结果集.
  # 3.1 账户A 扣钱
  sql1 = "update account set money = money - 1000 where id = 1;"
  row1 = cursor.execute(sql1) # 返回的是: 受影响的行数.
  conn.commit()   # 提交事务.
  
  # 3.2 模拟程序出Bug.
  print(10 / 0)
  
  # 3.3 账户B 收钱
  sql2 = "update account set money = money + 1000 where id = 2;"
  row2 = cursor.execute(sql2)     # 返回的是: 受影响的行数.
  conn.commit()   # 提交事务.
  
  # 4. 操作结果集, 看: 转账是否成功, 并提示.
  print('转账成功!' if row1 == 1 and row2 == 1 else '转账失败!')
  
  # 5. 释放资源.
  cursor.close()
  conn.close()
  ```

* Python代码-模拟转账-**加入事务**

  ```python
  """
  需求: PyMysql演示 转账, 无事务版, 看看会发生什么问题.
  
  遇到的问题:
      账号A 扣钱 成功, 但是 账号B 收钱 失败.
  产生原因:
      没有把 账号A扣钱 和 账号B收钱 这两个操作放在一起, 所以它们是两个独立的动作, 各不相关.
  解决方案:
      把 账号A扣钱 和 账号B收钱 这两个动作绑定到一起, 要么全部成功, 要么全部失败.
      这就可以通过 事务 来实现.
  """
  
  # 导包
  import pymysql
  
  # 1. 获取连接对象.
  conn = pymysql.connect(host='localhost', port=3306, user='root', password='123456', database='day10', charset='utf8')
  # 2. 获取游标对象.
  cursor = conn.cursor()
  
  try:
      # 3. 执行SQL语句, 获取结果集.
      # 3.0 开启事务, 即: 在提交或者回滚前, 所有的动作都是1个整体.
      conn.begin()    # 开启事务, 其实你不写, 也不影响, 但是按照规范写法, 要写上.
  
      # 3.1 账户A 扣钱
      sql1 = "update account set money = money - 1000 where id = 1;"
      row1 = cursor.execute(sql1) # 返回的是: 受影响的行数.
  
      # 3.2 模拟程序出Bug.
      # print(10 / 0)
  
      # 3.3 账户B 收钱
      sql2 = "update account set money = money + 1000 where id = 2;"
      row2 = cursor.execute(sql2)     # 返回的是: 受影响的行数.
  except:
      # 3.4 走这里, 说明出现问题, 即: 先事务回滚, 然后提示 转账失败.
      conn.rollback()
      print('转账失败!')
  else:
      # 3.5 走这里, 说明无问题, 转账成功.
      conn.commit()       # 提交事务
      print('转账成功!')
  finally:
      # 4. 释放资源.
      cursor.close()
      conn.close()
  ```

4.递归-入门

* 概述

  > 在Python中, 我们把 函数自己调用自己的情况, 称之为: 递归.

* 注意事项:

  1. 递归必须要有出口, 否则容易造成: 死递归.
  2. 递归调用次数不能过多, 否则容易造成: 死递归.
  3. 搞定递归很简单, 只要分析清楚 **出口** 和 **规律**即可.

* 示例代码

  ```python
  """
  递归简介:
      概述:
          在Python中, 我们把 函数自己调用自己的情况, 称之为: 递归.
  
      注意事项:
        1. 递归必须要有出口, 否则容易造成: 死递归.
        2. 递归调用次数不能过多, 否则容易造成: 死递归.
        3. 搞定递归很简单, 只要分析清楚 出口 和 规律即可.
  """
  
  # 案例: 递归代码演示
  
  # 1. 定义fun01()函数, 实现自己调用自己.
  # 计数器
  count = 0
  def fun01():
      # 设置修改全局变量的值.
      global count
      count += 1
      # 正常打印
      print(f'我是 fun01 函数, {count}')
  
      # 出口
      if count >= 100:
          return
  
      # 函数自己调用自己 => 递归.
      fun01()
  
  # 2. 在main方法中测试.
  if __name__ == '__main__':
      # 调用 fun01()函数
      fun01()
  ```

5.递归-求阶乘

* 代码演示

  ```python
  """
  需求: 计算5的阶乘.
  
  阶乘公式:
      5! = 5 * 4 * 3 * 2 * 1 = 5 * 4! = 120
  即:
      n! = n * (n - 1)!
  
  求5的阶乘, 思路分析:
      5! = 5 * 4 * 3 * 2 * 1          -------
           5 * 4!                       |
               4 * 3!                  规律
                   3 * 2!               |
                       2 * 1!         -------
                           1! = 1      出口
  """
  
  
  # 1. 定义函数 factorial(n), 用来计算 n的阶乘.
  def factorial(n):
      # 出口, 1! = 1
      if n == 1:
          return 1
  
      # 规律
      return n * factorial(n - 1)
  
  
  # 2. 在main方法中测试.
  if __name__ == '__main__':
      result = factorial(5)
      print(f'result: {result}')  # 120
  ```

* 图解

6.递归-不死神兔

* 代码演示

  ```python
  """
  案例: 不死神兔, 斐波那契数列.
  
  介绍:
      传说在很久很久以前, 有一个意大利青年, 叫: 斐波那契, 有一天, 他提出来一个非常有意思的问题:
      1. 假设1对小兔子, 一个月之后会长成1对大兔子.
      2. 每对大兔子, 每个月都会生1对小兔子.
      3. 问: 假设所有的兔子都不死的情况下, 1对小兔子, 1年(即: 12个月)之后会变成多少对兔子?
  
  分析流程:
      月份      大兔子对数       小兔子对数       当月兔子总对数
      1月          0               1               1
      2月          1               0               1
      3月          1               1               2
      4月          2               1               3
      5月          3               2               5
      6月          5               3               8
      ......
      12月         *               *               144
  
  大白话解释斐波那契数列:
      已知数列: 1, 1, 2, 3, 5, 8, 13, 21... 问: 第12个整数是多少?
  
  结论:
      出口: 前两个月(第1月, 第2月)的兔子对数 = 1,  即: m = 1 or m = 2时, 兔子对数 = 1
      规律: 从第3个月开始, 每月兔子对数 = 上月兔子对数 + 上上月兔子对数, 即:  m月兔子对数 = (m-1)月兔子对数 + (m-2)月兔子对数
  """
  # 1. 定义函数, 计算 month 月的兔子对数.
  def get_rabbit(m):
      # 出口, 前两个月的兔子对数 = 1
      # if m == 1 or m == 2:
      # if m <= 2:
      if m in [1, 2]:
          return 1  # 兔子对数
      # 规律: m月兔子对数 = (m-1)月兔子对数 + (m-2)月兔子对数
      return get_rabbit(m - 1) + get_rabbit(m - 2)
  
  
  # 2. 在main函数中调用
  if __name__ == '__main__':
      # 思路1: 递归版.
      num = get_rabbit(12)
      print(f'第12个月的兔子对数为: {num} 对!')
      print('-' * 30)
  
      # 思路2: 用 列表 实现, 已知列表 rabbit_list = [1, 1], 后续的每个值 = 前两个值之和, 问: 列表的第12个值是多少?
      # 1. 定义列表, 记录 每月的兔子对数.
      rabbit_list = []
      # 2. 因为前两个月的兔子对数都是1, 我们手动添加即可.
      rabbit_list.append(1)
      rabbit_list.append(1)
      # 3. 从第3个值(即: 第3月开始)有规律了, 当月 = 前两个值之和.
      for i in range(2, 12):  # i(索引)的值: 2 ~ 12, 包左不包右, 即: 2 ~ 11 分别表示 3月 ~ 12月
          # 4. 规律: 当前值 = 前两个值之和.
          rabbit_list.append(rabbit_list[i - 1] + rabbit_list[i - 2])
      # 5. 打印结果.
      print(rabbit_list)
      print(f'第12个月的兔子对数为: {rabbit_list[11]} 对!')
      print(f'第12个月的兔子对数为: {rabbit_list[len(rabbit_list) - 1]} 对!')
  
  ```

7.递归-爬楼梯

* 代码演示

  ```python
  """
  需求:
      假设需要爬n阶台阶才能到达顶层, 每次可以选择爬1阶或者2阶. 问: 共有几种方式可以到达顶层.
  例如:
      到达顶层的楼梯总台阶数             你可以选择的具体的爬楼梯的方案                 方案总数
          1                              1                                       1
          2                              11,2                                    2
          3                              21,12,111                               3
          4                              1111,112,121,211,22                     5
          5                              11111,1112,1121,1211,2111,122,212,221   8
          ......
  结论:
      出口: n=1  =>  1     n=2  =>  2
      规律: n >= 3,   n = (n - 1)的方案总数 + (n - 2)的方案总数
  """
  # 1. 定义函数, 递归实现 计算爬楼梯总方案数.'
  def climb_stairs(n):
      # 出口
      if n == 1:
          return 1        # 1个台阶, (爬楼梯)总方案数 = 1
      elif n == 2:
          return 2        # 2个台阶, (爬楼梯)总方案数 = 2
  
      # 规律
      return climb_stairs(n - 1) + climb_stairs(n - 2)
  
  
  # 2. 在main函数中测试.
  if __name__ == '__main__':
      # 思路1: 递归版.
      count = climb_stairs(6)
      print(f'爬楼梯总方案数为: {count}')
  
      # 思路2: for循环 + 列表版 实现. 
  ```

8.递归-打印文件夹

* 需求介绍

  > 键盘录入1个文件夹路径, 然后打印其所有的子级路径(包括子级的子级)

* 代码实现

  ```python
  """
  需求: 键盘录入1个文件夹路径, 打印该文件夹下所有文件(包括子文件夹)
  
  出口: 如果是文件, 就打印, 不递归.
  规律: 如果是文件夹, 就递归.
  """
  import os      # Operating  System, 系统模块, 里边定义的主要是和 文件, 文件夹(路径)相关的操作.
  
  # 1. 定义函数 print_dir(path), 打印 path 文件夹路径下所有的子级.
  def print_dir(path):
      """
      自定义函数, 打印path路径下 所有的子级, 包括子级的子级
      :param path: 要被打印的 文件夹路径
      :return: 无
      """
      # 1.1 判断path是否是 存在的 文件夹路径.
      if os.path.isdir(path):     # 假设: path = 'd:/src'
          # 1.2 走到这里, 说明是 存在的 文件夹路径, 就: 获取其所有的子级.
          child_names = os.listdir(path)  # ['aa', 'ai30', '代码打字练习词库.txt', '面试题.py']
          # 1.3 把上述的 子级文件(夹)的名字, 拼接成: 完整的 子级路径.
          for child_name in child_names:
              # 1.4 具体的拼接子级路径的动作, 即: 子级路径 = path + '/' + child_name
              child_path = path + '/' + child_name
              # 1.5 打印 子级路径
              print(child_path)
              # 1.6 判断当前的子级路径是否是 文件夹路径, 如果是文件夹, 就: 递归.
              if os.path.isdir(child_path):
                  # 递归
                  print_dir(child_path)
      else:
          # 1.3 走到这里, 说明路径非法.
          print('路径有误, 不是文件夹路径 或者 路径不存在, 程序结束!')
  
  
  # 2. 在main方法中, 测试.
  if __name__ == '__main__':
      print_dir('d:/src')
  
  
      print('-' * 30)
  
  
      # 3. 回顾 os 模块的函数.
      # 创建目录
      # os.mkdir('d:/aaa')
      # 改名
      # os.rename('d:/aaa', 'd:/xyz')
      # 删除目录, 必须是: 空目录
      # os.rmdir('d:/dest')
  
      # 查看目录下, 所有的子级(不包括子级的子级), 只能获取: 子级的名字, 而不是子级的目录.
      # list_dir = os.listdir('d:/src')
      # print(type(list_dir))   # <class 'list'>
      # print(list_dir)         # ['aa', 'ai30', '代码打字练习词库.txt', '面试题.py']
  
      # os.path.isdir(路径): 判断是否是 存在的 文件夹.
      print(os.path.isdir('d:/src/aa'))   # 存在, 且是文件夹.        True
      print(os.path.isdir('d:/src/bb'))   # 不存在.     False
      print(os.path.isdir('d:/src/面试题.py'))   # 存在的, 但是是文件. False
      print('-' * 30)
  
      # os.path.isfile(路径): 判断是否是 存在的 文件
      print(os.path.isfile('d:/src/面试题.py'))  # True, 存在, 且是文件.
      print(os.path.isfile('d:/src/abc.py'))    # False, 不存在, 是文件.
      print(os.path.isfile('d:/src/aa'))        # False, 存在, 是文件夹.
      print('-' * 30)
  
      # os.path.exists(路径): 判断是否是 存在的文件 或者文件夹.
      print(os.path.exists('d:/src/面试题.py'))  # True, 存在的 文件
      print(os.path.exists('d:/src/aa'))        # True, 存在的 文件夹.
      print(os.path.exists('d:/src/bb'))        # False, 不存在的.
      print('-' * 30)
  
      # os.path.basename(路径):
      print(os.path.basename('d:/src/面试题.py'))  # 面试题.py
      print(os.path.basename('d:/src/1.py'))      # 1.py
      print(os.path.basename('d:/src/aa'))        # aa
      print(os.path.basename('d:/src/bb'))        # bb
      print(os.path.basename('d:/ai30/base_class/hg/xyz'))    # xyz
  ```

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值