小菜鸟

java菜鸟号正在起航

Anaconda 虚拟环境完全指南:创建、管理与实战

Anaconda 的虚拟环境功能是解决 Python 项目依赖冲突的核心工具,尤其适合多项目并行开发(如同时维护 Python 3.6 的旧项目和 Python 3.11 的新项目)。

为什么要使用多个环境?

项目 需要的 Python 版本 需要的包
项目 A(TensorFlow 1.x) Python 3.6 tensorflow==1.15
项目 B(PyTorch 最新版) Python 3.9 torch>=2.0
项目 C(数据分析) Python 3.8 pandas, jupyter

如果不使用环境,这些依赖会冲突。使用环境可以完全隔离

环境创建:从基础到进阶

1. 基础创建:指定 Python 版本

创建环境时必须明确 Python 版本(避免默认版本不匹配),语法如下:

# 格式:conda create -n 环境名 python=具体版本
conda create -n data-analysis python=3.8  # 数据分析环境,Python 3.8
conda create -n tf2x python=3.7           # TensorFlow 2.x 环境,Python 3.7(适配性更高)
  • 版本选择原则:优先选择项目依赖包支持的稳定版本(如 PyTorch 2.0+ 推荐 Python 3.8-3.10)。
  • 命名规范:建议包含「项目类型 + Python 版本」(如 django32-py310),避免混淆。

2. 进阶创建:一键安装依赖包

创建环境时直接安装核心包,减少后续操作步骤:

# 创建环境并安装 numpy、pandas、jupyter(数据科学常用包)
conda create -n ds-py39 python=3.9 numpy pandas jupyter -y
# -y 参数:自动确认安装(无需手动输入 y)
  • 适用场景:新建项目时已知需要的核心依赖(如开发 Flask 项目时,可直接安装 flask gunicorn)。

  • 通道选择:若默认通道安装慢,可指定conda-forge通道(社区维护,包更全):

    conda create -n ml-py310 python=3.10 -c conda-forge scikit-learn xgboost

环境管理

1. 环境激活:跨平台差异

不同操作系统的激活命令一致,但终端显示略有不同:

阅读全文 »

Python 协程:从 yield 到 async/await,异步编程的进化之路

写 Python 做网络请求、爬虫、Web 服务的时候,你有没有遇到过这种情况:程序卡在某个地方等响应,CPU 明明闲着,但就是干等着?

协程就是解决这个问题的。它让你在等待 I/O 的时候去做别的事,而不是傻等。

协程到底是什么?跟线程有什么区别?

先回答一个问题:协程比线程轻量多少?

  • 一个线程的内存占用大约 8MB,一个协程大约 2KB
  • 线程切换涉及内核态,协程切换只在用户态
  • 一台服务器能跑几百个线程,但能跑几万个协程

协程是”协作式”的:它主动让出 CPU(遇到 await 的时候),而不是被操作系统强行打断。这意味着同一时刻只有一个协程在运行,不需要考虑锁的问题。

什么时候用协程? I/O 密集型任务——网络请求、文件读写、数据库查询。这些操作大部分时间在等外部响应,协程可以利用这些等待时间做别的事。

什么时候别用? CPU 密集型计算。协程解决不了计算慢的问题,这种场景应该用多进程。

协程的演进:从 yield 到 async/await

Python 的协程经历了几个阶段,了解一下演进脉络,能帮你理解为什么现在是 async/await

第一阶段:yield(生成器)

Python 2 时代,用生成器来模拟协程。yield 可以暂停函数、返回一个值,下次调用 next() 继续执行。

def generator():
    yield 1
    yield 2
    yield 3

g = generator()
next(g)  # 1
next(g)  # 2

这已经具备了”暂停-恢复”的能力,但用来做异步调度还太原始。

第二阶段:yield from(委托生成器,Python 3.3)

让一个生成器可以把执行委托给另一个生成器,解决了嵌套调用的问题。

def sub():
    yield 1
    yield 2

def main():
    yield from sub()   # 把控制权交给 sub
    yield 3

第三阶段:@asyncio.coroutine + yield from(Python 3.4)

Python 3.4 引入了 asyncio 库,用装饰器标记协程。

@asyncio.coroutine
def old_style():
    yield from asyncio.sleep(1)
    return "done"
阅读全文 »

Python 线程:什么时候该用,什么时候别用

写并发代码的时候,很多人第一反应就是”开线程”。

但 Python 的线程有个特殊的地方——GIL(全局解释器锁)。这东西让 Python 同一时刻只能有一个线程在执行 Python 字节码。所以:

  • I/O 密集型任务(网络请求、文件读写、数据库查询):多线程有用,线程在等 I/O 的时候 GIL 会释放
  • CPU 密集型任务(大量计算、图像处理):多线程没用,甚至更慢

一句话:Python 多线程适合”等”的场景,不适合”算”的场景。

创建线程:两种方式,用第一种就够了

方式1:直接传函数(最常用)

import threading
import time

def worker(name, delay):
    for i in range(3):
        print(f"{name}{i+1} 次")
        time.sleep(delay)

t1 = threading.Thread(target=worker, args=("A", 1))
t2 = threading.Thread(target=worker, args=("B", 2))

t1.start()
t2.start()

t1.join()  # 等 t1 结束
t2.join()  # 等 t2 结束
print("全部完成")

start() 启动线程,join() 等待线程结束。主线程如果不 join(),子线程可能还没来得及执行完,程序就退出了。

方式2:继承 Thread 类(封装复杂状态时用)

class DownloadTask(threading.Thread):
    def __init__(self, url):
        super().__init__()
        self.url = url
        self.result = None
    
    def run(self):
        # 线程启动后自动执行
        print(f"下载:{self.url}")
        time.sleep(2)
        self.result = f"{self.url} 完成"

task = DownloadTask("https://example.com/file.zip")
task.start()
task.join()
print(task.result)

日常开发用方式1就够了,方式2在需要把线程状态封装在对象里的时候用。

守护线程:主程序退出,它也跟着退

普通线程会阻止程序退出——主线程结束了,还要等所有子线程结束才退出。

守护线程(daemon=True)不一样:主线程一退出,守护线程直接被终止。

def background():
    while True:
        print("后台运行...")
        time.sleep(1)

t = threading.Thread(target=background, daemon=True)
t.start()

time.sleep(3)
print("主程序结束")  # 3秒后退出,守护线程自动终止
阅读全文 »

PHP8 引用传递和值传递:变量传递机制详解

在 PHP 中,函数参数的传递方式有两种:值传递(默认值)和引用传递。理解这两种机制的区别对于助于理解变量在函数内外的交互方式,避免意外的变量修改或性能问题。

值传递(Pass by Value)

值传递是 PHP 中默认的参数传递方式。当使用值传递时,函数会接收参数的副本,函数内部对参数的修改不会影响外部原始变量。

<?php
function increment($number) {
    $number++; // 修改的是参数的副本
    echo "函数内部:{$number}<br>"; // 输出:函数内部:11
}

$original = 10;
increment($original);
echo "函数外部:{$original}"; // 输出:函数外部:10(原始变量未改变)
?>

值传递的特点:

  • 函数内部操作的是原始值的副本,不影响外部变量
  • 适合传递简单类型(整数、字符串、布尔值等)
  • 传递大型变量(如大数组)时,会复制数据,可能消耗更多内存

引用传递(Pass by Reference)

引用传递允许函数接收变量的内存地址,而非值的副本。函数内部对参数的修改会直接影响外部原始变量,无需返回值即可同步变化。

引用传递通过在参数前添加 & 符号声明。

<?php
function incrementByReference(&$number) { // 参数前加&表示引用传递
    $number++; // 直接修改原始变量
    echo "函数内部:{$number}<br>"; // 输出:函数内部:11
}

$original = 10;
incrementByReference($original);
echo "函数外部:{$original}"; // 输出:函数外部:11(原始变量被修改)
?>
阅读全文 »

PHP8 类和接口:面向对象编程的核心

PHP8 对面向对象编程(OOP)进行了多项增强,引入了新特性并改进了现有功能,使类和接口的使用更加灵活和强大。本文将详细介绍 PHP8 中类和接口的定义、特性及新功能。

类的基本概念与定义

类是面向对象编程的基本构建块,用于封装数据和操作数据的方法。

1. 基本类定义

<?php
class Person {
    // 属性
    public $name;
    public $age;
    
    // 构造方法
    public function __construct(string $name, int $age) {
        $this->name = $name;
        $this->age = $age;
    }
    
    // 方法
    public function greet() {
        return "Hello, my name is {$this->name} and I'm {$this->age} years old.";
    }
    
    public function birthday() {
        $this->age++;
    }
}

// 创建对象
$person = new Person("John", 30);
echo $person->greet(); // 输出:Hello, my name is John and I'm 30 years old.

$person->birthday();
echo $person->greet(); // 输出:Hello, my name is John and I'm 31 years old.
?>

2. 属性与访问修饰符

PHP 支持三种访问修饰符,用于控制类成员的访问权限:

阅读全文 »
0%