oeasy Python 121[专业选修]列表_多维列表运算_列表相加_列表相乘

121用列表乘法控制鼓点

  • 这是 oeasy 系统化 Python 教程,从基础一步步讲,扎实、完整、不跳步。愿意花时间学,就能真正学会。[专业选修]列表_多维列表运算_列表相加_列表相乘

回忆

|------------------|-------------|
| 赋值方式 | 核心特点(大白话口诀) |
| ​​=​​ | 共用地址,一改全改 |
| ​​copy()​​ | 浅层独立,深层共用 |
| ​​deepcopy()​​ | 全部独立,互不干扰 |

  • 我想复制节奏
  • 这里面 有什么说法吗?🤔
直接相加
  • 两种节奏小节
  • 动词打次

  • 动动打次

    b1 = list("动次打次")
    b2 = list("动动打次")
    r1 = [b1] * 3 + [b2]
    song = r1 * 2

  • 节奏片段

  • 3个 动词打次 小节
  • 1个 动动打次 小节
  • 一首歌
  • 有两个 节奏片段
  • 将相加结果进行赋值
  • 想 对song[0] 直接赋值
直接赋值
复制代码
b1 = list("动次打次")
b2 = list("动动打次")
r1 = [b1] * 3 + [b2]
song = r1 * 2
song[0] = list("次次次次")
  • 第一个 小节
  • 鼓点变了
  • 如果想要 所有的
  • ​动词打次​ 都成 ​次次次次​ 呢?
变化
复制代码
b1 = list("动次打次")
b2 = list("动动打次")
r1 = [b1] * 3 + [b2]
song = r1 * 2
song[0][0:] = list("次" * 4)
  • 把 直接替换列表项
  • 换成了 切片赋值
  • 如果 有三首歌
  • 都重复 这套节奏 呢?
三首歌
  • 三首歌 节奏重复
  • 又增了 一层结构

    b1 = list("动次打次")
    b2 = list("动动打次")
    r1 = [b1] * 3 + [b2]
    song = r1 * 2
    songs = [song] * 3

  • 三首歌

  • 都指向 同一首歌 的 地址
  • 想要 只修改
  • 第一首歌的 第一小节
修改
复制代码
# 初始代码
b1 = list("动次打次")
b2 = list("动动打次")
r1 = [b1] * 3 + [b2]
song = r1 * 2
songs = [song] * 3

# 直接替换列表
songs[0][0] = list("次次次次")
  • 三首歌的第一小节 都修改了
  • 我只想修改 第一首的第一小节
浅拷贝
复制代码
# 初始化原始数据
b1 = list("动次打次")
b2 = list("动动打次")
r1 = [b1] * 3 + [b2]
song = r1 * 2
songs = [song] * 3  # 此时3首歌共享同一个song对象

# 关键步骤:先给第一首歌创建独立的浅拷贝,切断引用
songs[0] = songs[0].copy()  # 或 list(songs[0]) / songs[0][:]

# 现在修改第一首歌的第一小节,仅影响第一首
songs[0][0] = list("次次次次")
  • 确实只修改 第一首歌的 第一小节
  • 但这个结构还是特别乱
  • 我想给每个小节
  • 都分配独立空间
深拷贝
复制代码
from copy import deepcopy

# 基础节拍单元(单层列表,无嵌套)
beat_b1 = list("动次打次")
beat_b2 = list("动动打次")

# 用 deepcopy 替代 copy(),效果完全一致
song_verses = [
    deepcopy(beat_b1),  # 第1小节(独立,无引用)
    deepcopy(beat_b1),  # 第2小节(独立)
    deepcopy(beat_b1),  # 第3小节(独立)
    deepcopy(beat_b2)   # 第4小节(独立)
]
single_song = deepcopy(song_verses * 2)
three_song = deepcopy([single_song]) +  deepcopy([single_song]) + deepcopy([single_song])
  • 先 完成 基础节奏
  • 然后完成一首歌
最后的三首歌
  • three_song中的每个小节
  • 都有自己独立的位置
  • 互不影响
  • 但是比较 占用空间
  • 但是 pattern当中的 4个小节还是有重复
彻底独立
复制代码
from copy import deepcopy

# 基础节拍单元(单层列表,无嵌套)
beat_b1 = list("动次打次")
beat_b2 = list("动动打次")

# 用 deepcopy 替代 copy(),效果完全一致
song_verses = [
    deepcopy(beat_b1),  # 第1小节(独立,无引用)
    deepcopy(beat_b1),  # 第2小节(独立)
    deepcopy(beat_b1),  # 第3小节(独立)
    deepcopy(beat_b2)   # 第4小节(独立)
]
single_song = deepcopy(song_verses) + deepcopy(song_verses)
three_song = deepcopy([single_song]) +  deepcopy([single_song]) + deepcopy([single_song])
  • 能把这个写成代码吗?
代码
  • 三首歌
  • 24个小节
复制代码
import copy
import mido
from mido import Message, MidiFile, MidiTrack

# ===================== 1. 基础节拍定义(修正乐器映射) =====================
# 重新定义4/4拍的"动次打次"乐器映射(按你的要求)
# 动: 低音鼓 (Kick, MIDI 36) - 1拍
# 次: 闭合踩镲 (Hi-Hat, MIDI 42) - 1拍
# 打: 军鼓 (Snare, MIDI 38) - 1拍
beat_mapping = {
    '动': [36],    # 低音鼓 (Kick)
    '次': [42],    # 闭合踩镲 (Hi-Hat)
    '打': [38]     # 军鼓 (Snare)
}

# 基础节拍单元(标准4/4拍,每小节4拍)
beat_b1 = list("动次打次")  # 动(1拍)、次(1拍)、打(1拍)、次(1拍)
beat_b2 = list("动动打次")  # 动(1拍)、动(1拍)、打(1拍)、次(1拍)

# 用 deepcopy 确保每个小节独立
song_verses = [
    copy.deepcopy(beat_b1),  # 第1小节(4拍)
    copy.deepcopy(beat_b1),  # 第2小节(4拍)
    copy.deepcopy(beat_b1),  # 第3小节(4拍)
    copy.deepcopy(beat_b2)   # 第4小节(4拍)
]
single_song = copy.deepcopy(song_verses * 2)  # 完整单曲(8小节,32拍)
three_song = [
    copy.deepcopy(single_song),
    copy.deepcopy(single_song),
    copy.deepcopy(single_song)
]  # 三首连播

# ===================== 2. 生成MIDI文件核心逻辑(修正时序) =====================
def create_beat_midi(beat_sequence, filename="dongci_daci.mid", tempo=120):
    """
    生成标准4/4拍的打击乐MIDI文件
    :param beat_sequence: 嵌套的节拍列表
    :param filename: 输出MIDI文件名
    :param tempo: 速度(BPM),120对应每分钟120拍
    """
    # 创建MIDI文件和音轨
    mid = MidiFile()
    track = MidiTrack()
    mid.tracks.append(track)

    # 设置速度(120 BPM = 500000 微秒/拍)
    track.append(mido.MetaMessage('set_tempo', tempo=mido.bpm2tempo(tempo)))
    # 10号通道(索引9)是MIDI标准打击乐通道
    track.append(Message('program_change', channel=9, program=0, time=0))

    # 4/4拍的核心:每拍时长 = 480 ticks(MIDI标准四分音符)
    tick_per_beat = 480

    # 扁平化嵌套的节拍序列(转为一维列表,确保逐拍处理)
    flat_beats = []
    for song in beat_sequence:
        for bar in song:  # bar = 小节
            flat_beats.extend(bar)

    # 生成MIDI音符(关键修正:确保每拍严格占480 ticks)
    # 初始时间偏移为0
    current_time = 0
    for beat_char in flat_beats:
        if beat_char in beat_mapping:
            note = beat_mapping[beat_char][0]  # 每个节拍对应一个乐器
            # 音符开启(note_on):在current_time时刻触发
            track.append(Message(
                'note_on', 
                channel=9, 
                note=note, 
                velocity=80,  # 音量(64-100为常用范围)
                time=current_time
            ))
            # 音符关闭(note_off):间隔1拍(480 ticks)后释放
            track.append(Message(
                'note_off', 
                channel=9, 
                note=note, 
                velocity=80, 
                time=tick_per_beat
            ))
            # 重置时间偏移,确保下一拍从0开始
            current_time = 0

    # 保存MIDI文件
    mid.save(filename)
    print(f"标准4/4拍MIDI文件已生成:{filename}")

# ===================== 3. 执行生成 =====================
if __name__ == "__main__":
    # 生成三首连播的标准4/4拍MIDI
    create_beat_midi(three_song, "three_songs_4_4_beat.mid", tempo=120)
新想法
  • 想要 闭擦踩满
闭擦踩满
复制代码
import copy
import mido
from mido import Message, MidiFile, MidiTrack

# ===================== 1. 基础节拍定义(重新设计数据结构) =====================
# 定义MIDI打击乐音符编号
KICK = 36    # 底鼓
SNARE = 38   # 军鼓
HI_HAT = 42  # 闭合踩镲

# 重新定义4/4拍的基础小节结构(完全按你的要求)
# 第0拍: kick + hh | 第1拍: hh | 第2拍: snare + hh | 第3拍: hh
basic_bar = [
    [KICK, HI_HAT],  # 第0拍(动):底鼓+踩镲
    [HI_HAT],        # 第1拍(次):仅踩镲
    [SNARE, HI_HAT], # 第2拍(打):军鼓+踩镲
    [HI_HAT]         # 第3拍(次):仅踩镲
]

# 变体小节(保留差异化,第0/1拍都是kick+hh)
variant_bar = [
    [KICK, HI_HAT],  # 第0拍:底鼓+踩镲
    [KICK, HI_HAT],  # 第1拍:底鼓+踩镲(变体)
    [SNARE, HI_HAT], # 第2拍:军鼓+踩镲
    [HI_HAT]         # 第3拍:仅踩镲
]

# 构建歌曲结构(用deepcopy确保每个小节独立)
song_verses = [
    copy.deepcopy(basic_bar),   # 第1小节
    copy.deepcopy(basic_bar),   # 第2小节
    copy.deepcopy(basic_bar),   # 第3小节
    copy.deepcopy(variant_bar)  # 第4小节(变体)
]
single_song = copy.deepcopy(song_verses * 2)  # 完整单曲(8小节)
three_song = [
    copy.deepcopy(single_song),
    copy.deepcopy(single_song),
    copy.deepcopy(single_song)
]  # 三首连播

# ===================== 2. 生成MIDI文件核心逻辑(适配新结构) =====================
def create_beat_midi(beat_sequence, filename="dongci_daci.mid", tempo=120):
    """
    生成标准4/4拍的打击乐MIDI文件(适配新的拍子列表结构)
    :param beat_sequence: 嵌套的节拍列表(每个拍子是乐器编号列表)
    :param filename: 输出MIDI文件名
    :param tempo: 速度(BPM)
    """
    # 创建MIDI文件和音轨
    mid = MidiFile()
    track = MidiTrack()
    mid.tracks.append(track)

    # 设置速度(120 BPM = 500000 微秒/拍)
    track.append(mido.MetaMessage('set_tempo', tempo=mido.bpm2tempo(tempo)))
    # 10号通道(索引9)是MIDI标准打击乐通道
    track.append(Message('program_change', channel=9, program=0, time=0))

    # 4/4拍核心:每拍时长 = 480 ticks(MIDI标准四分音符)
    tick_per_beat = 480

    # 扁平化嵌套的节拍序列(转为一维的"拍子列表")
    flat_beats = []
    for song in beat_sequence:
        for bar in song:        # bar = 小节(包含4个拍子)
            for beat in bar:    # beat = 拍子(包含1个/多个乐器)
                flat_beats.append(beat)

    # 生成MIDI音符(适配新结构:直接遍历乐器列表)
    current_time = 0
    for beat_instruments in flat_beats:
        # 触发当前拍子的所有乐器(note_on)
        for instrument in beat_instruments:
            track.append(Message(
                'note_on',
                channel=9,
                note=instrument,
                velocity=80,  # 音量(适中)
                time=current_time
            ))
        # 释放当前拍子的所有乐器(note_off),间隔1拍
        for instrument in beat_instruments:
            track.append(Message(
                'note_off',
                channel=9,
                note=instrument,
                velocity=80,
                time=tick_per_beat if instrument == beat_instruments[0] else 0
            ))
        # 重置时间偏移,确保下一拍从0开始
        current_time = 0

    # 保存MIDI文件
    mid.save(filename)
    print(f"标准4/4拍MIDI文件已生成:{filename}")

# ===================== 3. 执行生成 =====================
if __name__ == "__main__":
    # 生成三首连播的MIDI文件
    create_beat_midi(three_song, "three_songs_4_4_full_hh.mid", tempo=120)
缝合(zip)
  • 列表还有一种运算方式
  • 叫做缝合(zip)

    students = ["oeasy", "o2z", "o3z"]
    math = [95, 96, 97]
    chinese = [91, 92, 93]
    score = list(zip(students, math, chinese))
    print(score)

  • zip 可以 把 不同列表

  • 缝到一起
  • 同样位置的元素
  • 缝到一个新元素里面
查询帮助
  • 查询关于zip的帮助
  • help(zip)
  • 缝合出来的列表可以用lambda排序吗?
排序
复制代码
sorted_by_chinese = sorted(score, key=lambda x: x[2])
sorted_by_chinese
  • 确实可以按照 语文成绩 排序
总结
  • 这次我们 玩了节奏
  • 可以 让节奏成为套路

  • 也可以 让节奏独立空间

    students = ["oeasy", "o2z", "o3z"]
    math = [95, 96, 97]
    chinese = [91, 92, 93]
    score = list(zip(students, math, chinese))
    print(score)

  • 缝合操作

  • 把列表里 同样位置的元素
  • 缝合 在一起
  • 缝合里 的列表项
  • 怎么不是 ​中括号​ 包裹的呢??🤔
相关推荐
资深数据库专家2 小时前
总账EBS 应用服务器1 的监控分析
java·网络·数据库
房开民2 小时前
可变参数模板
java·开发语言·算法
t***5442 小时前
如何在现代C++中更有效地应用这些模式
java·开发语言·c++
_深海凉_2 小时前
LeetCode热题100-最小栈
java·数据结构·leetcode
m0_678485452 小时前
CSS如何控制表格单元格边框合并_通过border-collapse实现
jvm·数据库·python
不知名的忻2 小时前
Morris遍历(力扣第99题)
java·算法·leetcode·morris遍历
m0_748839492 小时前
如何用组合继承模式实现父类方法复用与子类属性独立
jvm·数据库·python
qq_334563553 小时前
PHP源码是否依赖特定芯片组_Intel与AMD平台差异【操作】
jvm·数据库·python
daidaidaiyu3 小时前
一文学习入门 ThingsBoard 开源物联网平台
java·mqtt·spring