大数据开发学习Day2
一、shell脚本进阶
- 变量定义、参数循环
if条件判断、for循环
写一个脚本:遍历当前目录下的所有.log文件,打印文件的行数
#!/bin/bash
# 遍历当前目录下所有.log文件
for file in *.log; do
# 检查是否存在.log文件(防止无匹配文件时出错)
if [ -f "$file" ]; then
# 使用wc -l统计行数,通过cut提取数字部分
lines=$(wc -l < "$file")
echo "文件: $file 行数: $lines"
fi
done
- 扩展:常用的文件测试操作符
# 文件测试
-e "$file" # 文件是否存在(存在即为真)
-f "$file" # 是否是普通文件
-d "$file" # 是否是目录
-s "$file" # 文件是否非空(大小>0)
-r "$file" # 是否可读
-w "$file" # 是否可写
-x "$file" # 是否可执行
# 字符串比较
[ -z "$str" ] # 字符串是否为空
[ -n "$str" ] # 字符串是否非空
[ "$a" = "$b" ] # 字符串是否相等
[ "$a" != "$b" ] # 字符串是否不等
# 数字比较
[ $a -eq $b ] # 等于 (equal)
[ $a -ne $b ] # 不等于 (not equal)
[ $a -gt $b ] # 大于 (greater than)
[ $a -lt $b ] # 小于 (less than)
[ $a -ge $b ] # 大于等于
[ $a -le $b ] # 小于等于
将上述代码保存为count_log_lines.sh
赋予执行权限:chmod +x count_log_lines.sh
运行脚本:./count_log_lines.sh
二、SQL 进阶:4 道中等题,练聚合和排序
重点理解GROUP BY的筛选逻辑,还有DATEDIFF这类日期函数的用法
183.从不订购的客户

select c.name AS customers
from customers c
left join orders o ON c.id = o.customerid
where o.customerid is null
197.上升的温度

select b.id
from weather a, weather b
where datediff(b.recorddate, a.recorddate) = 1 and b.temperature > a.temperature
596.超过 5 名学生的课

select class
from courses
group by class
having count(class) >= 5
三、Python 进阶:剩下的 3 个高频面试点
1、迭代器 vs 生成器的区别,生成器怎么节省内存?

在Python中,迭代器和生成器都是用于处理数据序列的工具,但它们在实现方式、内存管理和使用场景上有显著差异。
迭代器与生成器的基本区别
- 定义方式:
迭代器:需要手动实现_iter_() 和_next_() 方法。例如,创建一个自定义迭代器类:
class MyIterator:
def __init__(self, max_val):
self.max_val = max_val
self.current = 0
def __iter__(self):
return self
def __next__(self):
if self.current < self.max_val:
self.current += 1
return self.current
raise StopIteration
生成器:使用 yield 关键字定义函数,自动实现迭代协议(包括 iter() 和 next())。例如:
def my_generator(max_val):
current = 0
while current < max_val:
current += 1
yield current
生成器本质上是一种特殊的迭代器,但写法更简洁,无需显式管理状态
-
内存占用:
迭代器在初始化时可能需要预加载数据(如列表),但生成器采用惰性求值(lazy evaluation),一次只生成一个值,避免存储整个序列34。例如,处理大型数据集时,生成器表达式 (i for i in range(1000000)) 比列表 [i for i in range(1000000)] 占用更少内存 -
自动处理:
生成器在结束时自动抛出 StopIteration 异常,简化了错误处理;而迭代器需要手动实现这一点
总结关键区别:

- 生成器节省内存的原理:
生成器节省内存的核心在于 惰性求值(lazy evaluation) 和 按需生成(on-demand generation)。具体原理如下:-
惰性求值:生成器不会一次性计算和存储所有结果,而是在每次调用 next() 时动态生成一个值。例如,在生成器函数 my_generator(1000) 中,每次 next(g) 只计算并返回当前值(如1、2、3),而不是预先创建包含1000个元素的列表。这减少了内存峰值占用。
-
内存效率对比:
- 对于大数据集(如处理100万个元素):
- 列表方式:需要一次性分配内存存储所有元素,占用空间为 O(n)。
- 生成器方式:只存储当前状态(如计数器和局部变量),占用空间为
O(1)。例如:
- 对于大数据集(如处理100万个元素):
-
# 列表:内存占用高
big_list = [x for x in range(1000000)] # 占用大量内存
# 生成器:内存占用低
big_gen = (x for x in range(1000000)) # 几乎不占用额外内存
这种机制特别适合处理无限序列或大型文件(如日志分析),避免内存溢出
- 实际应用:生成器常用于协程(coroutine),利用 yield 保存执行状态,实现高效并发
2、深拷贝 vs 浅拷贝的区别,什么时候会用到?

在编程中,对象拷贝(copy)是创建对象的副本的过程,主要分为浅拷贝(shallow copy)和深拷贝(deep copy)。它们的关键区别在于内存管理和对嵌套对象的处理方式,这直接影响程序的可靠性和性能。
- 基本概念
浅拷贝:
只复制对象本身(外层对象),但不复制嵌套的子对象(如列表中的列表、字典中的字典)。拷贝后,新对象与原对象共享嵌套对象的引用。修改嵌套对象时,原对象和拷贝对象都会受到影响,因为它们引用同一个嵌套对象。示例:使用copy.copy()或列表切片进行浅拷贝。
import copy
a = [1, 2, [3, 4]] # 原始列表,包含嵌套列表
b = copy.copy(a) # 浅拷贝
b[2].append(5) # 修改嵌套列表
print(a) # 输出: [1, 2, [3, 4, 5]],原对象也被修改
print(b) # 输出: [1, 2, [3, 4, 5]]
这里,a和b是不同对象(a is b为False),但嵌套列表的引用相同(a[2] is b[2]为True)
深拷贝:
递归复制所有对象,包括外层和嵌套子对象。创建完全独立的副本,修改一个对象不会影响另一个。示例:使用copy.deepcopy()进行深拷贝。
import copy
a = [1, 2, [3, 4]] # 原始列表
b = copy.deepcopy(a) # 深拷贝
b[2].append(5) # 修改嵌套列表
print(a) # 输出: [1, 2, [3, 4]],原对象未受影响
print(b) # 输出: [1, 2, [3, 4, 5]]
这里,a和b的所有部分都是独立对象(a[2] is b[2]为False)
-
主要区别

-
使用场景
根据对象结构和需求选择合适的拷贝方式:- 使用浅拷贝的场景:
- 对象结构简单,无嵌套或子对象不可变(如整数、字符串、元组)。例如,复制一个只包含不可变元素的列表时,浅拷贝更高效。
- 需要节省内存或避免冗余复制时,因为浅拷贝不会创建嵌套对象的新副本。
- 示例:当处理只包含数字的列表时,使用list.copy()或切片[:]即可
- 使用深拷贝的场景:
- 对象包含可变的嵌套子对象(如列表中的列表、字典中的字典),且需要完全独立修改时。例如,在备份复杂数据结构或避免共享状态时。
- 在递归操作或数据持久化中,确保原始数据不被意外修改。
- 示例:复制一个多层嵌套的字典或自定义对象时,必须用深拷贝来防止副作用。
- 特殊注意事项:
- 对于不可变对象(如元组、字符串),浅拷贝和深拷贝效果相同,Python会复用内存(id相同)。
- 对于可变对象(如列表、字典),深拷贝是安全的,但性能开销大;浅拷贝更快,但需谨慎处理嵌套修改。
- 使用浅拷贝的场景:
-
代码示例对比
import copy
# 原始对象:包含可变嵌套列表
original = [1, ["a", "b"]]
# 浅拷贝
shallow_copy = copy.copy(original)
shallow_copy[1].append("c")
print("浅拷贝后 - Original:", original) # 输出: [1, ['a', 'b', 'c']]
print("浅拷贝后 - Copy:", shallow_copy) # 输出: [1, ['a', 'b', 'c']]
# 深拷贝
deep_copy = copy.deepcopy(original)
deep_copy[1].append("d")
print("深拷贝后 - Original:", original) # 输出: [1, ['a', 'b', 'c']](未变)
print("深拷贝后 - Copy:", deep_copy) # 输出: [1, ['a', 'b', 'c', 'd']]
此示例显示,浅拷贝共享嵌套列表,修改影响原对象;深拷贝则完全独立。
- 总结
- 区别核心:浅拷贝共享嵌套引用,深拷贝创建完全独立副本。
- 选择原则:
- 优先浅拷贝:简单对象、性能敏感场景。
- 使用深拷贝:复杂嵌套结构、需数据隔离时。
- 实际开发中,用copy模块的copy()和deepcopy()函数实现拷贝,避免用赋值(=)操作符,因为它只创建新引用。
3、Python 的内存管理机制:引用计数、垃圾回收

Python的内存管理采用引用计数为主、垃圾回收为辅的双重机制,实现自动内存管理。以下是核心原理和工作流程:
- 引用计数机制:实时内存回收
引用计数是Python内存管理的第一道防线,核心规则如下:
计数规则:- 对象被创建时引用计数=1(如a = [1,2])
- 新增引用时计数+1(如b = a)
- 引用销毁时计数-1(如del b)
- 计数归零时立即释放内存
import sys
obj = [1, 2, 3] # 计数=1
print(sys.getrefcount(obj)) # 输出2(sys.getrefcount调用产生临时引用)
ref = obj # 计数+1 → 3
del ref # 计数-1 → 2
del obj # 计数-1 → 0 → 内存释放
优势: 实时性高:计数归零立即释放内存、低开销:增减计数操作效率高
缺陷: 无法处理循环引用(如a.ref=b; b.ref=a)
-
垃圾回收机制:解决循环引用
当引用计数失效时,垃圾回收(Garbage Collection, GC)介入,采用三层策略:

-
分代回收(Generational GC):
-
对象按存活时间分为三代:
-
回收频率:第0代 > 第1代 > 第2代
-
新对象优先在第0代检查,长期存活对象逐步升级
-
-
标记-清除(Mark-Sweep):
-
标记阶段:从根对象(全局变量、调用栈)出发,标记所有可达对象
-
清除阶段:回收未被标记的对象(不可达对象)
-
专攻循环引用问题
-
-
引用计数辅助:
- GC运行时复用引用计数数据加速标记过程
-
-
双机制协同工作流程

-
关键特性与优化
- 不可变对象复用:
- 小整数(-5~256)、短字符串会被缓存复用
- 示例:a=100; b=100 → a is b为True
- 内存池机制:
-
小于256KB的对象使用Python内存池分配
-
减少系统调用次数,提升小对象分配效率
-
- GC触发条件:
-
阈值触发:当某代对象数量超过阈值(默认700/10/10)
-
手动触发:gc.collect()
-
退出触发:程序结束时回收所有内存
-
- 性能权衡:
-
引用计数:高频轻量操作
-
垃圾回收:低频重量操作(可能暂停程序)
-
- 不可变对象复用:
-
循环引用处理示例
import gc
import objgraph
class Node:
def __init__(self):
self.ref = None
# 创建循环引用
a = Node()
b = Node()
a.ref = b # a引用b
b.ref = a # b引用a → 循环引用
del a, b # 引用计数仍为1
# 手动触发GC回收
gc.collect()
print(objgraph.count('Node')) # 输出0 → 对象已回收
- 总结:内存管理双机制对比

Python通过引用计数实现高效实时回收,辅以垃圾回收解决循环引用,两者协同实现自动内存管理。开发者需注意避免循环引用和内存泄漏,在必要时使用gc模块进行诊断
四、前置准备:PySpark 环境搭建
今天提前把 PySpark 的环境搭好,之后学组件直接就能用,不用再花时间
from pyspark.sql import SparkSession
spark = SparkSession.builder.appName("test").getOrCreate()
print(spark.version)

五、算法题
- 20.有效的括号
class Solution(object):
def isValid(self, s):
"""
:type s: str
:rtype: bool
"""
xuanze = {"(":")",
"{":"}",
"[":"]",
}
jieguo_list = []
# 假定数据为 ({})
if len(s)%2 != 0:
return False
s_list = list(s)
for s_str in s_list:
if s_str not in xuanze.keys():
if jieguo_list != []:
if s_str == xuanze[str(jieguo_list[-1])]:
del jieguo_list[-1]
else:
return False
else:
return False
else:
jieguo_list.append(s_str)
if jieguo_list == []:
return True
else:
return False
更多推荐
所有评论(0)