Python大模型基础

发布时间:2026/7/13 4:20:42
Python大模型基础 一. python1.python概述1.1 计算机的组成计算机由运算器、控制器、存储器、输入设备和输出设备等五个逻辑部件组成1.2 计算机语言的发展简史机器语言(二进制代码)--汇编语言(符号语言)--高级语言(C、C、Java、PHP、JavaScript、Python等)1.3 python的特点1.3.1 python优点简单易学广泛的标准库和大量的第三方模块可移植可扩展免费开源1.3.2 python缺点解释语言,运行速度慢代码不能加密1.4 python应用Web应用开发(FastAPI、Django、Flask)网络爬虫系统网络运维数据分析与科学计算(Numpy、Scipy)人工智能与机器学习(TensorFlow、Keras)1.5 Python解释器CPython(Python 3.x后)IPythonPyPyJythonIronPython2. 基础知识2.1 注释对代码解释说明提高代码可读性屏蔽掉暂时不需要的代码定位程序出错的位置2.1.1 注释分类单行注释行注释多行注释块注释2.2 变量2.2.1 标识符命名规则只能包含字母、数字和下划线且不能以数字开头区分大小写不要和关键字重复2.3 进制及转换二进制以0b或0B开头表示 bin(dec)八进制以0o开头表示 oct(dec)十六进制以0x或0X开头表示此处的A-F不区分大小写 hex(dec)2.4 数据类型基本数据类型数值(整数int、浮点数float、复数complex、布尔bool)字符串str容器数据类型列表list元组tuple集合set字典dict特殊数据类型None表示空值或缺失值只有一个值 None。常用于函数没有返回值时或者表示变量没有被赋值。不可变数据3 个Number数字、String字符串、Tuple元组可变数据3 个List列表、Dictionary字典、Set集合2.4.1 int整型num1 True num2 10 print(type(num1)) # class bool print(type(num2)) # class int print(type(num1) type(num2)) # False print(isinstance(num1, bool)) # True print(isinstance(num1, int)) # TruePython3中bool是int的子类 print(isinstance(num2, int)) # True ​2.4.2 float浮点型2.4.3 bool布尔型FalseNone00.0所有的空容器空列表、空元组、空字典、空集合、空字符串2.4.4 str 字符串3. 流程控制语句顺序分支单分支多分支嵌套分支match case语句三目运算符(表达式1 if 判断条件 else 表达式2)while 循环for 循环(遍历可迭代对象如列表或字符串)continue(跳过当前循环块中的剩余语句继续进行下一轮循环)break(for 或while循环终止)4.容器数据类型4.1 序列Sequence一种基本且核心的数据结构它允许我们以有序的方式存储和操作数据。序列可以包含不同类型的元素并且支持通过索引来访问和修改这些元素。常见的序列类型包括列表List、元组Tuple、字符串String4.2 列表List一个可变的、有序的元素集合用 [] 定义数据之间使用 , 分隔索引或下标访问元素可以是不同的类型访问列表通过索引获取列表中元素列表切片向列表中添加元素(append/insert/extend)修改列表中元素(下标/切片)列表推导式squares [x**2 for x in range(10) if x % 2 0]zip() 函数可将多个可迭代对象中对应元素打包为一个个元组4.3 字符串 str替换str.replace(old,new[,max])分割str.split(x)合并(x作为分隔符)x.join(seq)4.4 元组 Tuple不可变的、有序的元素集合,不能对元组中的元素进行修改操作使用 () 定义数据之间使用,分隔索引或下标访问元素可以是不同的类型4.5 集合Set无序的且不包含重复元素用 {} 定义数据之间使用 , 分隔也可以使用set()定义没有索引所以不能通过切片方式访问集合元素元素可以是不同的类型可以进行数学上的集合操作如并集、交集和差集适用于需要快速成员检查、消除重复项和集合运算的场景添加元素.add()删除元素.remove()遍历集合 in4.6 字典Dictionary无序的键值对集合键是唯一的而值可以重复,没有索引用 {}或dict() 定义键key和值value使用 : 连接每个键值对之间使用 , 分隔通过键来获取对应的值值可以取任何数据类型但键必须是不可变的如字符串、数字、元组5. 函数定义函数使用 def 关键字先定义再调用若函数的形参是定长参数可以通过 * 和 ** 对列表、元组、字典等解包传参/ 前的参数必须使用位置传参* 后的参数必须用关键字传参global : 修改全局变量nonlocal : 内部作用域修改外部作用域(函数中)的变量用 lambda 来定义匿名函数 (lambda 参数列表: 表达式)def operator(a, b): return a b ​ def function(a, b, operator): return operator(a, b) ​ print(function(1, 2, lambda x, y: x y))6. 文件操作纯文本文件二进制文件(如图片文件jpg、png视频文件avi等)r : 读 ; w : 写 ; a : 追加写 ; x : 创建文件写 ; b : 二进制 ; t : 文本模式 ; : 读写read([size]) 可以从文件中读取数据size 表示要从文件中读取的数据的长度编码模式b单位是字节编码模式t单位是字符如果没有传入 size 则读取文件中所有的数据readline([size]) 可以从文件中读取整行数据也可以通过 size 设置读取数据的长度readlines([size]) 读取所有行并返回列表若给定 size0返回总和大约为 size 字节的行 实际读取值可能比 size 大6.1 with上下文管理器try: with open(test.txt, w) as f: f.write(a) finally: print(文件是否关闭, f.closed) # 文件是否关闭 True7. 面向对象之类和对象类是创建对象的模板对象是类的实例Instance类名一般使用大驼峰命名法类方法在类中通过 classmethod 定义第一个参数为cls代表类本身静态方法在类中通过 staticmethod 定义 , 不访问实例属性或类属性只依赖于传入的参数魔法方法 : 方法名中有两个前缀下划线和两个后缀下划线的方法8. 面向对象之三大特性8.1 封装将变量和函数写入类中的操作即为封装即类中封装了属性和方法通过双下划线定义私有属性或私有方法8.1.1 property方法转换为属性 : 可通过property装饰器将一个方法转换为属性来调用。转换后可直接使用 .方法名 来使用而无需使用 .方法名()class Person: ​ def __init__(self, name): self.name name ​ property def eat(self): print(f{self.name} is eating...) ​ p Person(张三) p.eat # 张三 is eating...只读属性 : 将方法名设置为去掉双下划线的私有属性名方法中返回私有属性class Person: ​ def __init__(self, name): self.__name name ​ property def name(self): return self.__name ​ p Person(张三) print(p.name) # 张三 p.name 李四 # 报错读写属性:将方法名设置为去掉双下划线的私有属性名方法中返回私有属性class Person: ​ def __init__(self, name): self.__name name ​ property def name(self): return self.__name ​ name.setter def name(self, name): self.__name name ​ p Person(张三) print(p.name) # 张三 ​ p.name 李四 print(p.name) # 李四8.2 继承子类继承父类中的属性和方法实现代码重用。子类可以新增自己特有的方法也可以重写父类的方法子类不能继承父类的私有属性和私有方法支持多继承子类可以在类中使用 super().方法名() 或 父类名.方法名() 来调用父类的方法8.2.1 方法解析顺序mro类名.mro访问类的继承链来查看方法解析顺序8.2.2 方法重写在子类中定义与父类方法重名的方法调用时会调用子类中重写的方法8.3 多态9. 错误和异常语法错误 : 程序解析时遇到的错误异常 : 运行期检测到的错误用 try except 语句来捕获异常并处理用 raise 来抛出异常assert用于判断一个表达式在表达式条件为False的时候触发异常常用于调试程序def int_add(x, y): assert isinstance(x, int) and isinstance(y, int), 参数类型错误 return x y ​ print(int_add(1, 2)) # 3 print(int_add(1, 2)) # AssertionError: 参数类型错误10. 模块和包使用from import *导入模块时在被导入的模块中使用all设置哪些内容可以被导入。all的设置只针对使用 from import * 导入模块时有效__all__ [num,add] # 内容必须要用引号引起来 ​ num 100 num1 200 _str1abc def add(a, b): 求两个数的和 return a b10.1 常见的标注库(包)os : 多种操作系统接口sys : 系统相关的形参和函数time : 时间的访问和转换\datetime : 操作日期和时间的类math : 数学函数random : 生成伪随机数re : 正则表达式匹配操作jsoncollections :10.2 引入第三方库阿里云Simple Index豆瓣http://pypi.douban.com/simple/清华大学Simple Indexpip install 包名临时使用其他源 : pip install -i Simple Index 包名永久修改源 : pip config set global.index-url Simple Index打包自己的库并安装pip install setuptools在包外创建一个 setup.py文件from distutils.core import setup ​ setup( namegraphic, # 需要打包的名字 version1.0, # 版本 py_modules[graphic.circle, graphic.rectangle], # 需要打包的模块 )pip install setuptools11.Python高级语法11.1 浅拷贝与深拷贝直接赋值对象的引用别名不产生拷贝浅拷贝拷贝父对象不会拷贝对象的内部的子对象。拷贝后只有第一层是独立的深拷贝完全拷贝了父对象及其子对象。拷贝后所有层都是独立的使用 copy() 对其浅拷贝。使用 id() 查看列表地址和列表中各个元素的地址使用 copy 模块的 deepcopy() 函数11.2 迭代器可迭代对象可以直接作用于 for 循环的数据类型有以下几种容器如 list 、 tuple 、 dict 、 set 、 str 等。generator 包括生成器和带 yield 的generator function判断是否是可迭代对象Iterable)from collections.abc import Iterable ​ print(isinstance([], Iterable)) # True print(isinstance((), Iterable)) # True print(isinstance(set(), Iterable)) # True print(isinstance({}, Iterable)) # True print(isinstance(100, Iterable)) # True print(isinstance(100, Iterable)) # False判断是否是迭代器Iteratorfrom collections.abc import Iterator ​ print(isinstance([], Iterator)) # False print(isinstance((), Iterator)) # False print(isinstance(set(), Iterator)) # False print(isinstance({}, Iterator)) # False print(isinstance(100, Iterator)) # False print(isinstance((x for x in range(10)), Iterator)) # Truelist [1, 2, 3] it iter(list) # 创建迭代器对象 print(next(it)) # 输出迭代器的下一个元素,1 print(next(it)) # 2 print(next(it)) # 3 print(next(it)) # StopIterationlist [1, 2, 3] it iter(list) # 创建迭代器对象 for i in it: print(i)11.3 生成器一个用于创建迭代器的简单而强大的工具。它的写法类似于标准的函数但当它要返回数据时会使用 yield 语句。当在生成器函数中使用 yield 语句时函数的执行将会暂停并将 yield 后的表达式作为当前迭代的值返回每次调用生成器的 next() 方法或使用 for 循环进行迭代时函数会从上次暂停的地方继续执行它会记住上次执行语句时的所有数据值直到再次遇到 yield 语句。这样生成器函数可以逐步产生值而不需要一次性计算并返回所有结果11.3.1 创建生成器使用推导式创建生成器generator (x for x in range(5)) # 创建生成器 print(generator) # generator object genexpr at 0x0000026C2066CB80 for x in generator: print(x)使用函数创建生成器def fibo(): # 斐波那契数列 a, b 0, 1 while True: yield b a, b b, a b ​ f fibo() print(next(f)) # 1 print(next(f)) # 1 print(next(f)) # 2 print(next(f)) # 3 print(next(f)) # 511.4 闭包构建闭包的条件 :外部函数内定义一个内部函数内部函数用到外部函数中的变量外部函数将内部函数作为返回值11.5 装饰器不修改原有函数代码的基础上动态地增加或修改函数的功能from math import sqrt ​ def func(x): 开根号 return sqrt(x) ​ def decorator(f): def inner(x): x abs(x) # 求x的绝对值 return f(x) ​ return inner ​ func decorator(func) print(func(-4)) # 2.0 ​from math import sqrt ​ def decorator(f): def inner(x): x abs(x) # 求x的绝对值 return f(x) ​ return inner ​ decorator def func(x): 开根号 return sqrt(x) ​ print(func(-4)) # 2.012. 进程与线程并发 : 单个 CPU 处理多个任务。各个任务交替执行一段时间并行 : 多个 CPU 同时执行多个任务12.1 多进程进程是操作系统进行资源分配的基本单位操作系统中一个正在运行的程序或软件就是一个进程每个进程都有自己独立的一块内存空间12.1.1 创建进程使用Process()创建进程自定义Process子类创建进程,继承Process当需要启动大量子进程时可以使用进程池Pool()12.2 多线程线程是处理器任务调度和执行的基本单位一个进程至少有一个线程也可以运行多个线程多个线程之间可共享数据线程运行出错异常后如果没有捕获可能会导致整个进程崩溃多线程是指在同一进程中同时执行多个任务12.2.1 创建进程使用Thread创建线程自定义Thread子类创建线程,继承Thread线程池创建ThreadPoolExecutor()13. 协程Coroutine协程Coroutine是Python中处理高并发场景的强大工具尤其在 I/O 密集型任务中表现出色。协程是一种用户态的轻量级线程它不像线程那样由操作系统调度而是由程序自身控制所以也叫微线程13.1 协程的优势调度者协程由程序自身进行调用主动让出CPU给其他协程通过 await 关键字轻量级创建成本极低一个线程程可以轻松创建数万个协程无锁机制同一时间只有一个协程运行无需锁保护共享资源与线程相比协程避免了操作系统级别的上下文切换开销只有极小的函数调用开销在处理大量 I/O 操作时效率更高13.2 async和await13.2.1 定义协程函数async def使用async def关键字定义的函数就是协程函数它返回一个协程对象import asyncio ​ # 这是一个协程函数 async def hello(): print(Hello) # await 关键字只能在协程函数内部使用 await asyncio.sleep(1) # 模拟I/O操作 print(World) ​ # 调用 hello() 不会立即执行只会返回一个协程对象 coro_obj hello() print(type(coro_obj)) # 输出: class coroutine13.3 运行协程事件循环import asyncio import time ​ async def my_func(name, delay): print(f任务 {name} 开始) # 遇到 await协程让出控制权 await asyncio.sleep(delay) print(f任务 {name} 完成) return f任务 {name} 结果 ​ async def main_sync(): print(--- 串行执行总耗时 1s 2s 3s---) # await 串行执行必须等待前一个 task 完成才会开始下一个 await my_func(A, 1) await my_func(B, 2) ​ if __name__ __main__: start_time time.time() asyncio.run(main_sync()) print(f串行耗时: {time.time() - start_time:.2f}秒) ​ 输出: --- 串行执行总耗时 1s 2s 3s--- 任务 A 开始 任务 A 完成 任务 B 开始 任务 B 完成 串行耗时: 3.02秒13.4 事件循环(Event Loop) 详述事件循环是协程并发模型的核心它是负责调度、管理和执行所有协程的“心脏”13.4.1 注册任务asyncio.run(main_coro)(顶级注册启动程序)asyncio.create_task(coro)(最常用实现并发)await coro(隐式注册实现串行)13.5 实现并发Task与asyncio.gather()import asyncio import time ​ async def my_func(name, delay): print(f任务 {name} 开始...) await asyncio.sleep(delay) print(f任务 {name} 完成.) return f结果 of {name} ​ async def main_concurrent(): print(--- 并发执行总耗时 max(1s, 2s) 2s---) # 1. 创建 Task协程被包装成任务立即启动在后台运行 task1 asyncio.create_task(my_func(C, 1)) task2 asyncio.create_task(my_func(D, 2)) # 2. 等待 Task 完成await asyncio.gather() 等待所有任务结束 # 注意这里的 await 只是等待结果任务本身早已开始并发运行。 results await asyncio.gather(task1, task2) print(f所有结果: {results}) ​ if __name__ __main__: start_time time.time() asyncio.run(main_concurrent()) print(f并发总耗时: {time.time() - start_time:.2f}秒) 输出: --- 并发执行总耗时 max(1s, 2s) 2s--- 任务 C 开始... 任务 D 开始...二、LinuxLinux系统中一切皆文件APT常用命令list - 根据名称列出软件包search -搜索软件包描述show - 显示软件包细节install - 安装软件包reinstall - 重新安装软件包remove - 移除软件包update - 更新可用软件包列表1.常用基本命令帮助命令: man ls - 查看ls命令的帮助信息 help cd - 查看cd命令的帮助信息 常用快捷键: Ctrl L - 清屏彻底清屏是clear Ctrl U - 清除当前敲的命令 文件命令 pwd - 显示当前工作目录的绝对路径 touch test/a/note.txt - 创建空文件 cp -r test/b/ ./ - 递归复制 rm -r b/ - 递归删除 mv a.txt b.txt - 重命名 mv test/a.txt ./ - 移动文件 cat -n a.txt - 查看文件显示行号 tail - 输出文件尾部内容(默认10行; -n 5:尾部5行;-F:实时输出) echo - 输出内容到控制台 vim命令 yy - 复制光标当前一行 p - 箭头移动到目的行粘贴 u - 撤销上一步 dd - 删除光标当前行 :wq! - 强制保存退出 用户管理命令  su 用户名称 - 切换用户只能获得用户的执行权限不能获得环境变量 用户管理命令  su 用户名称 - 切换用户只能获得用户的执行权限不能获得环境变量  su - 用户名称 - 切换到用户并获得该用户的环境变量及执行权限  useradd [-g 组名] 用户名 - 添加新用户到某个组 passwd 用户名 - 设置用户密码 id 用户名 - 查看用户是否存在 cat /etc/passwd - 查看创建了哪些用户 userdel 用户名 - 删除用户但保存用户主目录  userdel -r 用户名 - 删除用户的同时删除与用户相关的所有文件 文件权限 0 123 456 789 类型(d:目录/l:链接文档) 属主(rwx:读写执行) 属组 其他用户权限 chmod 改变权限 u:所有者 g:所有组 o:其他人 a:所有人(u、g、o的总和)rwx4217 chmod u-x,ox passwd - 修改文件属主用户执行权限,并使其他用户具有执行权限 chmod -R 777 test - 压缩ctar -zcvf xxx.tar.gz 解压xtar -zxvf xxx.tar.gz 查看磁盘空间:df -h三.Docker官网地址Docker: Accelerated Container Application Development提供了一套标准化的环境打包与运行方案把代码、依赖、配置、系统环境全部打包在一起形成一个可移植的容器添加国内镜像源 registry-mirrors: [ https://docker.xuanyuan.me, https://docker.1ms.run, https://docker.m.daocloud.io ]1.Docker优势可移植性可伸缩性隔离性2.Docker核心概念2.1 镜像Image—类比「软件安装包」只读不能直接修改可作为模板创建多个容器包含了运行某个应用程序所需的全部环境比如操作系统、软件依赖、配置文件等2.2 容器Container—类比「已安装好的软件」镜像的运行实例是一个可读写的独立环境相当于“把安装包安装好后能直接使用的软件”可启动、停止、删除多个容器相互独立容器删除后里面的数据默认会丢失可通过“数据卷”保留后续会讲2.3 仓库Repository—类比「手机应用商店」官方仓库地址https://hub.docker.com/用于存放和下载镜像的平台2.4 数据卷Volume—— 类比「电脑的D盘专门存数据」数据卷是Docker中用于持久化数据的工具相当于“给容器挂一个独立的存储盘”容器删除后数据卷中的数据不会丢失2.5 核心工具Dockerfile和Docker Compose2.5.1 Dockerfile —自定义镜像的“构建脚本”一个纯文本文件无后缀名包含一系列简单的指令用于告诉Docker“如何构建自定义镜像”2.5.2 Docker Compose —多容器的“一键管理工具”一个用于管理多容器的工具通过一个yaml格式的配置文件固定命名为docker-compose.yml定义多个关联容器的配置比如镜像、端口、依赖关系执行一条命令就能一键启动、停止所有容器# 核心功能一键启动MySQL容器Python应用容器实现容器间通信、数据持久化 ​ services: # 容器1MySQL服务容器命名为mysql-db容器间可通过该名称访问 mysql-db: # 基础镜像指定MySQL 8.0.45版本稳定性高适配Python连接 image: mysql:8.0.45 # 重启策略容器意外关闭/服务器重启时自动重启保证服务可用性 restart: always # 环境变量配置MySQL核心参数无需手动进入容器修改 environment: MYSQL_ROOT_PASSWORD: 123456 # root用户密码自定义Python连接需一致 MYSQL_DATABASE: test_db # 自动创建初始数据库无需手动建库 # 字符编码配置适配MySQL 8.0避免中文乱码/连接报错 MYSQL_INITDB_ARGS: --character-set-serverutf8mb4 --collation-serverutf8mb4_unicode_ci # 端口映射本地9999端口 → 容器内3306端口避免与本地MySQL端口冲突 ports: - 9999:3306 # 数据卷挂载Windows本地D盘mysql-data目录 → 容器内MySQL数据目录持久化数据 volumes: - D:/mysql-data:/var/lib/mysql # 加入自定义网络与Python容器互通容器间通信的基础 networks: - app-network # 健康检查判断MySQL服务是否真正就绪而非仅容器启动解决连接拒绝问题 healthcheck: test: [CMD, mysqladmin, ping, -h, localhost, -uroot, -p123456] # 检测MySQL是否可ping通 interval: 3s # 每3秒检查一次 timeout: 3s # 单次检查超时时间3秒 retries: 10 # 最多重试10次覆盖MySQL启动初始化时间 start_period: 5s # 容器启动后延迟5秒再开始检查避免过早检测 ​ # 容器2Python应用容器命名为python-app业务代码运行载体 python-app: # 构建规则基于当前目录下的Dockerfile构建自定义Python镜像 build: . # 依赖条件等待mysql-db容器的健康检查通过后再启动本容器核心修复解决MySQL未就绪问题 depends_on: mysql-db: condition: service_healthy # 仅当MySQL服务就绪时才启动Python容器 # 环境变量传递MySQL连接信息Python代码可通过os.getenv获取无需硬编码 environment: MYSQL_HOST: mysql-db # 容器间通信地址直接填MySQL容器名无需IP MYSQL_PORT: 3306 # MySQL容器内端口非本地9999 MYSQL_USER: root # 与MySQL容器的用户名一致 MYSQL_PASSWORD: 123456 # 与MySQL容器的密码一致修改需同步 MYSQL_DB: test_db # 与MySQL容器自动创建的数据库名一致 # 加入自定义网络与MySQL容器在同一网络才能通信 networks: - app-network ​ # 自定义网络桥接模式Docker默认保证两个容器相互可见、可访问 networks: app-network: driver: bridge ​3.基础命令docker --version docker compose version ​ docker pull mysql:8.0.45 ​ 查看镜像 docker images ​ ocker run -d --name mysql-db -p 9999:3306 -e MYSQL_ROOT_PASSWORD123456 mysql:8.0.45 ​ 查看容器 docker ps ​四.FastAPI一个现代、快速高性能的Web框架用于构建API类型提示type hints和异步编程asyncio能力使得代码易于编写、阅读和维护有自动交互式文档基于 OpenAPI 规范和 JSON Schema、数据验证、依赖注入Dependency Injection等功能from fastapi import APIRouter ​ # 实例化一个 APIRouter可指定前缀所有路由自动加上 /users router APIRouter( prefix/users, tags[用户管理] # 文档中归类为「用户管理」 ) ​ # 定义用户相关路由 router.get(/) def get_all_users(): return {message: 获取所有用户列表} ​from fastapi import APIRouter ​ router APIRouter( prefix/items, tags[商品管理] # 文档中归类为「商品管理」 ) ​ # 定义商品相关路由 router.get(/) def get_all_items(): return {message: 获取所有商品列表} ​from fastapi import FastAPI from routers import user, item # 导入子模块路由 import uvicorn ​ app FastAPI(title路由分发示例) ​ # 挂载用户路由所有 /users 开头的请求由 user.router 处理 app.include_router(user.router) ​ # 挂载商品路由所有 /items 开头的请求由 item.router 处理 app.include_router(item.router) ​ # 主应用自身也可以定义路由 app.get(/) def root(): return {message: 欢迎访问主页面} ​ if __name__ __main__: # 直接在代码中启动uvicorn服务器 uvicorn.run( appmain:app, # 指定要运行的FastAPI应用实例 host0.0.0.0, # 允许外部访问本地可通过127.0.0.1或localhost访问 port8000, # 端口号 reloadTrue # 开发模式代码修改后自动重启生产环境需去掉 ) ​五. SQLAlchemy1.ORMORMObject-Relational Mapping对象关系映射是一种编程技术它将数据库中的表结构映射为编程语言中的类将表中的行映射为类中的对象让开发者可以用面向对象的方式操作数据库而无需直接编写 SQL 语句。屏蔽数据库差异同一套代码可适配多种数据库MySQL、PostgreSQL 等无需修改核心逻辑简化开发流程用类、对象、方法替代 SQL 语句降低数据库操作的学习成本提高代码可读性将数据库操作与业务逻辑融合代码更符合面向对象思维自动处理类型转换无需手动转换数据库字段与Python类型如MySQL的INT与Python的int