Python技术迷

上个月空降了个总监,听说200万从某大厂挖来的,他一来要大换血。团队开始站队:一波人疯狂表忠心,一波人消极抵抗,我选择观望。

刚看到个贴子,说公司空降来个高薪总监,一来就要“换血”,有人抢着表忠心,有人摆烂对着干,楼主选择先看戏。三个月一盘点:喊得最响的那批,确实被“重用”了,但基本都在干又累又难背锅的活;原地硬刚的,慢慢被边缘化。

Image

我觉得这事吧,关键不是站哪队,而是先想明白:你能提供什么价值,底线在哪。一上来就跪舔,短期看是机会,长期容易变成“免费苦力”;硬刚领导,在大多数公司又是九死一生。

从我的角度看,楼主现在的“观望”没问题,但别只是吃瓜,要悄悄把几件事做扎实:手里的活做到不可或缺,多记录自己的成果,少卷情绪,多留后路。

面试题:活跃用户

有天产品拍着我肩膀说:“能不能给我拉一个‘活跃用户列表’,就是那种连续好多天都来玩的用户?”听起来挺简单的,对吧?但你真要写代码,就得先把“活跃”这俩字说清楚。

我下面就按 Python 来聊一版常见定义:连续打卡型活跃用户,顺带把思路和代码都走一遍。

有一堆登录日志,每条长这样:

(user_id, login_date)
比如:
("u1", "2025-01-01")
("u1", "2025-01-02")
...

要求:找出在某段时间内,至少有 k 天连续登录的用户。 比如 k = 5,那 u1 在 1 日、2 日、3 日、4 日、5 日都来了,就算“活跃用户”。

现实里这个定义很好用:游戏的“连续登录奖励”,社区的“连续签到”,本质都是同一个事。

你想象一下用肉眼看日志:

  1. 先把同一个用户的日期拎在一起

  2. 把这些日期按时间排好序

  3. 从前往后扫,数一数连续的天数

  • 如果今天是昨天 + 1 天,那 streak++
  • 否则 streak 从 1 重新算
  • 一旦某个 streak ≥ k,这个用户就可以标记为“活跃”

  • 这个过程,其实就是我们要写进代码里的逻辑,没有什么高深的算法名词,就是“按人分组 + 排序 + 扫一遍”。

    直接上代码,先看整体,再拆细节:

    from collections import defaultdict
    from datetime import datetime, timedelta


    deffind_active_users(logs, k=5):
    """
        logs: 列表,每个元素是 (user_id, date_str)
              date_str 形如 "2025-01-01"
        k: 连续登录的天数阈值,比如 5 天
        返回: 满足条件的 user_id 列表
        """


    # 1. 先把每个用户对应的所有登录日期收集起来,用 set 去重
        user_dates = defaultdict(set)
    for user_id, date_str in logs:
    # 把字符串转成 date 对象,方便做加减
            d = datetime.strptime(date_str, "%Y-%m-%d").date()
            user_dates[user_id].add(d)

        active_users = []

    # 2. 对每个用户,检查它的连续登录情况
    for user_id, date_set in user_dates.items():
    # set 里是无序的,先排个序
            days = sorted(date_set)

    if len(days) < k:
    # 连登录记录都没 k 天,不可能连续 k 天,直接跳过
    continue

            streak = 1# 当前连续天数,从第一天开始算

    # 3. 从第二个日期开始往后扫
    for i in range(1, len(days)):
    # 如果这一天刚好是前一天 + 1 天,说明连续
    if days[i] == days[i - 1] + timedelta(days=1):
                    streak += 1
    else:
    # 中断了,重新计数
                    streak = 1

    # 只要达到 k,就可以认定是活跃用户,没必要再往后算了
    if streak >= k:
                    active_users.append(user_id)
    break

    return active_users

    你可以简单跑个小例子:

    if __name__ == "__main__":
        logs = [
            ("u1", "2025-01-01"),
            ("u1", "2025-01-02"),
            ("u1", "2025-01-03"),
            ("u1", "2025-01-04"),
            ("u1", "2025-01-05"),  # u1 连续 5 天

            ("u2", "2025-01-01"),
            ("u2", "2025-01-03"),
            ("u2", "2025-01-04"),  # u2 有断档

            ("u3", "2025-01-10"),
            ("u3", "2025-01-11"),
            ("u3", "2025-01-12"),
            ("u3", "2025-01-13"),
            ("u3", "2025-01-14"),
            ("u3", "2025-01-20"),  # u3 也连续 5 天
        ]

        print(find_active_users(logs, k=5))
    # 输出类似:['u1', 'u3']

    到这儿,一个“连续 k 天登录用户”的算法就算落地了。

    别看代码挺朴素,其实复杂度也可以算一算:

    1. 收集数据那一段:遍历所有日志一次,假设日志条数是 n,就是 O(n)
    2. 对每个用户的日期排序:假设用户总数是 m,每个用户有 d_i 条记录 总体复杂度大致是 Σ d_i log d_i,大约可以看成 O(n log n)
    3. 每个用户再线性扫一遍自己的日期:总和还是 O(n)

    所以主导复杂度就是那一轮排序:**O(n log n)**。 大部分业务场景,这个级别已经挺能打了,几百万行日志都没啥问题。

    随便提几个经常踩坑的点:

    1. 同一天重复多条登录记录

    • 比如用户一天打开了十几次 app,日志就一大堆
    • 我们用 set 去重,就是为了只保留“这天来过就行”,不按次数算
  • 日志可能没按时间排序

    • 所以每个用户自己的那一堆日期都要 sorted 一下,不要迷信“日志本来就是按时间写的”
  • 活跃定义随业务变

    • 有时候是“连续 k 天登录”
    • 有时候是“最近 30 天里活跃 ≥ 15 天”
    • 有时候甚至是“最近一周下过单就算活跃” 其实改起来也不难,关键是把“活跃”这个条件从代码里抽出来,写成清晰的 if/规则就行。

    比如如果你要的是“最近 7 天里登录过就算活跃用户”,代码可以变成这样:

    deffind_active_users_in_last_n_days(logs, n=7, today_str="2025-01-15"):
        today = datetime.strptime(today_str, "%Y-%m-%d").date()
        threshold = today - timedelta(days=n - 1)

        active_set = set()

    for user_id, date_str in logs:
            d = datetime.strptime(date_str, "%Y-%m-%d").date()
    if d >= threshold:
                active_set.add(user_id)

    return list(active_set)

    这个版本就更简单:不用排序,也不用算连续,时间复杂度直接 O(n),就是扫一遍。

    -END-

    我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html

    🔥虎哥私藏精品🔥

    虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB