Python列表(List)介绍
文章目录
- Python列表(List)介绍
- C++中存储多种类型的方法
-
- [1. 使用 `std::any` (C++17)](#1. 使用
std::any(C++17)) - [2. 使用 `std::variant` (C++17)](#2. 使用
std::variant(C++17)) - [3. 使用继承和多态](#3. 使用继承和多态)
- [4. 使用结构体和枚举](#4. 使用结构体和枚举)
- [5. 比较各种方法的优缺点](#5. 比较各种方法的优缺点)
- [6. 综合示例:JSON-like数据结构](#6. 综合示例:JSON-like数据结构)
- 总结
- [1. 使用 `std::any` (C++17)](#1. 使用
列表(List)是Python中最常用的数据结构之一,它是一个有序、可变(可修改)的元素集合。
基本特性
- 有序 - 元素有固定的顺序
- 可变 - 可以添加、删除或修改元素
- 可包含任意类型 - 同一个列表中可以包含不同类型的数据
- 可重复 - 元素可以重复出现
代码示例
1. 创建列表
python
# 创建空列表
empty_list = []
empty_list2 = list()
# 创建包含元素的列表
numbers = [1, 2, 3, 4, 5]
fruits = ['apple', 'banana', 'cherry']
mixed = [1, 'hello', 3.14, True]
print(f"空列表: {empty_list}")
print(f"数字列表: {numbers}")
print(f"混合类型列表: {mixed}")
2. 访问列表元素
python
fruits = ['apple', 'banana', 'cherry', 'date', 'elderberry']
# 通过索引访问
print(fruits[0]) # 输出: apple
print(fruits[2]) # 输出: cherry
# 负数索引(从末尾开始)
print(fruits[-1]) # 输出: elderberry
print(fruits[-2]) # 输出: date
# 切片操作
print(fruits[1:3]) # 输出: ['banana', 'cherry']
print(fruits[:3]) # 输出: ['apple', 'banana', 'cherry']
print(fruits[2:]) # 输出: ['cherry', 'date', 'elderberry']
print(fruits[::2]) # 输出: ['apple', 'cherry', 'elderberry'](步长为2)
3. 修改列表
python
colors = ['red', 'green', 'blue']
# 修改元素
colors[1] = 'yellow'
print(colors) # 输出: ['red', 'yellow', 'blue']
# 添加元素
colors.append('purple') # 在末尾添加
print(colors) # 输出: ['red', 'yellow', 'blue', 'purple']
colors.insert(1, 'orange') # 在指定位置插入
print(colors) # 输出: ['red', 'orange', 'yellow', 'blue', 'purple']
# 删除元素
removed = colors.pop() # 删除并返回最后一个元素
print(f"删除的元素: {removed}")
print(colors) # 输出: ['red', 'orange', 'yellow', 'blue']
colors.remove('yellow') # 删除指定元素
print(colors) # 输出: ['red', 'orange', 'blue']
del colors[0] # 删除指定索引的元素
print(colors) # 输出: ['orange', 'blue']
4. 列表操作
python
# 合并列表
list1 = [1, 2, 3]
list2 = [4, 5, 6]
combined = list1 + list2
print(f"合并后的列表: {combined}") # 输出: [1, 2, 3, 4, 5, 6]
# 重复列表
repeated = list1 * 3
print(f"重复3次: {repeated}") # 输出: [1, 2, 3, 1, 2, 3, 1, 2, 3]
# 检查元素是否存在
if 3 in list1:
print("3在列表中")
# 获取列表长度
print(f"list1长度: {len(list1)}") # 输出: 3
5. 列表方法
python
numbers = [5, 2, 8, 1, 9, 3]
# 排序
numbers.sort() # 升序排序
print(f"升序排序: {numbers}") # 输出: [1, 2, 3, 5, 8, 9]
numbers.sort(reverse=True) # 降序排序
print(f"降序排序: {numbers}") # 输出: [9, 8, 5, 3, 2, 1]
# 反转
numbers.reverse()
print(f"反转: {numbers}") # 输出: [1, 2, 3, 5, 8, 9]
# 查找元素索引
index = numbers.index(5)
print(f"元素5的索引: {index}") # 输出: 3
# 统计元素出现次数
count_list = [1, 2, 2, 3, 2, 4]
count = count_list.count(2)
print(f"2出现的次数: {count}") # 输出: 3
# 清空列表
count_list.clear()
print(f"清空后: {count_list}") # 输出: []
6. 列表推导式
python
# 创建平方数列表
squares = [x**2 for x in range(1, 6)]
print(f"平方数列表: {squares}") # 输出: [1, 4, 9, 16, 25]
# 筛选偶数
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
even_numbers = [x for x in numbers if x % 2 == 0]
print(f"偶数列表: {even_numbers}") # 输出: [2, 4, 6, 8, 10]
# 转换元素类型
str_numbers = [str(x) for x in numbers]
print(f"字符串列表: {str_numbers}") # 输出: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
7. 嵌套列表(多维列表)
python
# 创建二维列表(矩阵)
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
print(f"矩阵: {matrix}")
print(f"第二行: {matrix[1]}") # 输出: [4, 5, 6]
print(f"第二行第三列: {matrix[1][2]}") # 输出: 6
# 遍历二维列表
print("遍历矩阵:")
for row in matrix:
for element in row:
print(element, end=' ')
print()
8. 列表复制
python
original = [1, 2, 3, [4, 5]]
# 浅复制
shallow_copy = original.copy()
shallow_copy2 = original[:] # 另一种浅复制方式
# 深复制(需要导入copy模块)
import copy
deep_copy = copy.deepcopy(original)
# 修改原始列表中的嵌套列表
original[3][0] = 99
print(f"原始列表: {original}") # 输出: [1, 2, 3, [99, 5]]
print(f"浅复制: {shallow_copy}") # 输出: [1, 2, 3, [99, 5]](受影响)
print(f"深复制: {deep_copy}") # 输出: [1, 2, 3, [4, 5]](不受影响)
9. 列表解包
python
# 基本解包
a, b, c = [1, 2, 3]
print(f"a={a}, b={b}, c={c}") # 输出: a=1, b=2, c=3
# 扩展解包
first, *middle, last = [1, 2, 3, 4, 5]
print(f"first={first}, middle={middle}, last={last}") # 输出: first=1, middle=[2, 3, 4], last=5
总结
Python列表是一个功能强大且灵活的数据结构,常用于:
- 存储有序的数据集合
- 需要频繁修改的数据
- 实现栈、队列等数据结构
- 分组和聚合数据
列表在Python编程中无处不在,掌握好列表的使用是Python编程的基础。
C++中存储多种类型的方法
你说得完全正确!C++是静态类型语言,但现代C++提供了多种方式来处理需要在同一容器中存储不同类型数据的需求。以下是几种主要方法:
1. 使用 std::any (C++17)
std::any 可以存储任意类型的值,类似于Python的动态类型。
cpp
#include <iostream>
#include <vector>
#include <any>
#include <string>
#include <typeinfo>
int main() {
std::vector<std::any> mixed_data;
// 存储不同类型的数据
mixed_data.push_back(42); // int
mixed_data.push_back(3.14159); // double
mixed_data.push_back(std::string("Hello")); // std::string
mixed_data.push_back("C-string"); // const char*
mixed_data.push_back(true); // bool
// 访问数据
for (const auto& item : mixed_data) {
if (item.type() == typeid(int)) {
std::cout << "整数: " << std::any_cast<int>(item) << std::endl;
} else if (item.type() == typeid(double)) {
std::cout << "浮点数: " << std::any_cast<double>(item) << std::endl;
} else if (item.type() == typeid(std::string)) {
std::cout << "字符串: " << std::any_cast<std::string>(item) << std::endl;
} else if (item.type() == typeid(const char*)) {
std::cout << "C字符串: " << std::any_cast<const char*>(item) << std::endl;
} else if (item.type() == typeid(bool)) {
std::cout << "布尔值: " << std::boolalpha
<< std::any_cast<bool>(item) << std::endl;
}
}
// 使用try-catch避免异常
std::cout << "\n安全访问示例:" << std::endl;
for (const auto& item : mixed_data) {
try {
// 尝试作为int访问
int value = std::any_cast<int>(item);
std::cout << "作为int: " << value << std::endl;
} catch (const std::bad_any_cast&) {
// 不是int类型
std::cout << "不是int类型" << std::endl;
}
}
return 0;
}
2. 使用 std::variant (C++17)
std::variant 是一种类型安全的联合体,可以存储一组预定义类型中的一种。
cpp
#include <iostream>
#include <vector>
#include <variant>
#include <string>
#include <optional>
// 定义variant类型
using VariantType = std::variant<int, double, std::string, bool>;
int main() {
std::vector<VariantType> mixed_data;
// 添加数据
mixed_data.push_back(42); // int
mixed_data.push_back(3.14159); // double
mixed_data.push_back("Hello World"); // const char* 转换为 std::string
mixed_data.push_back(true); // bool
// 使用std::visit访问
auto print_visitor = [](const auto& value) {
using T = std::decay_t<decltype(value)>;
if constexpr (std::is_same_v<T, int>) {
std::cout << "整数: " << value << std::endl;
} else if constexpr (std::is_same_v<T, double>) {
std::cout << "浮点数: " << value << std::endl;
} else if constexpr (std::is_same_v<T, std::string>) {
std::cout << "字符串: " << value << std::endl;
} else if constexpr (std::is_same_v<T, bool>) {
std::cout << "布尔值: " << std::boolalpha << value << std::endl;
}
};
std::cout << "使用std::visit:" << std::endl;
for (const auto& item : mixed_data) {
std::visit(print_visitor, item);
}
// 使用index()和get()访问
std::cout << "\n使用index()和get():" << std::endl;
for (const auto& item : mixed_data) {
switch (item.index()) {
case 0: // int
std::cout << "整数: " << std::get<0>(item) << std::endl;
break;
case 1: // double
std::cout << "浮点数: " << std::get<1>(item) << std::endl;
break;
case 2: // std::string
std::cout << "字符串: " << std::get<2>(item) << std::endl;
break;
case 3: // bool
std::cout << "布尔值: " << std::boolalpha
<< std::get<3>(item) << std::endl;
break;
}
}
// 使用std::get_if安全访问
std::cout << "\n使用std::get_if安全访问:" << std::endl;
for (const auto& item : mixed_data) {
if (auto int_ptr = std::get_if<int>(&item)) {
std::cout << "整数: " << *int_ptr << std::endl;
} else if (auto double_ptr = std::get_if<double>(&item)) {
std::cout << "浮点数: " << *double_ptr << std::endl;
} else if (auto str_ptr = std::get_if<std::string>(&item)) {
std::cout << "字符串: " << *str_ptr << std::endl;
} else if (auto bool_ptr = std::get_if<bool>(&item)) {
std::cout << "布尔值: " << std::boolalpha << *bool_ptr << std::endl;
}
}
return 0;
}
3. 使用继承和多态
这是面向对象的经典方法,通过基类指针存储派生类对象。
cpp
#include <iostream>
#include <vector>
#include <memory>
#include <string>
#include <typeinfo>
// 基类
class DataItem {
public:
virtual ~DataItem() = default;
virtual void print() const = 0;
virtual std::string typeName() const = 0;
virtual std::unique_ptr<DataItem> clone() const = 0;
};
// 模板派生类
template<typename T>
class TypedDataItem : public DataItem {
T value_;
public:
explicit TypedDataItem(const T& value) : value_(value) {}
void print() const override {
std::cout << value_;
}
std::string typeName() const override {
return typeid(T).name();
}
std::unique_ptr<DataItem> clone() const override {
return std::make_unique<TypedDataItem<T>>(value_);
}
const T& get() const { return value_; }
T& get() { return value_; }
};
// 特化模板以提供更好的类型名
template<>
std::string TypedDataItem<int>::typeName() const { return "int"; }
template<>
std::string TypedDataItem<double>::typeName() const { return "double"; }
template<>
std::string TypedDataItem<std::string>::typeName() const { return "string"; }
template<>
std::string TypedDataItem<bool>::typeName() const { return "bool"; }
template<>
void TypedDataItem<bool>::print() const {
std::cout << std::boolalpha << value_;
}
int main() {
std::vector<std::unique_ptr<DataItem>> data_list;
// 添加不同类型的数据
data_list.push_back(std::make_unique<TypedDataItem<int>>(42));
data_list.push_back(std::make_unique<TypedDataItem<double>>(3.14159));
data_list.push_back(std::make_unique<TypedDataItem<std::string>>("Hello"));
data_list.push_back(std::make_unique<TypedDataItem<bool>>(true));
// 遍历和访问
std::cout << "使用多态:" << std::endl;
for (const auto& item : data_list) {
std::cout << "类型: " << item->typeName() << ", 值: ";
item->print();
std::cout << std::endl;
}
// 动态类型转换
std::cout << "\n动态类型转换:" << std::endl;
for (const auto& item : data_list) {
if (auto int_item = dynamic_cast<TypedDataItem<int>*>(item.get())) {
std::cout << "找到整数: " << int_item->get() << std::endl;
} else if (auto double_item = dynamic_cast<TypedDataItem<double>*>(item.get())) {
std::cout << "找到浮点数: " << double_item->get() << std::endl;
}
// 可以继续添加其他类型的检查
}
return 0;
}
4. 使用结构体和枚举
对于简单的场景,可以使用枚举和联合体(或std::variant)结合。
cpp
#include <iostream>
#include <vector>
#include <string>
#include <variant>
// 方法1:枚举 + variant
struct Data {
enum class Type { INT, DOUBLE, STRING, BOOL };
Type type;
std::variant<int, double, std::string, bool> value;
Data(int v) : type(Type::INT), value(v) {}
Data(double v) : type(Type::DOUBLE), value(v) {}
Data(const std::string& v) : type(Type::STRING), value(v) {}
Data(const char* v) : type(Type::STRING), value(std::string(v)) {}
Data(bool v) : type(Type::BOOL), value(v) {}
void print() const {
switch (type) {
case Type::INT:
std::cout << std::get<int>(value);
break;
case Type::DOUBLE:
std::cout << std::get<double>(value);
break;
case Type::STRING:
std::cout << std::get<std::string>(value);
break;
case Type::BOOL:
std::cout << std::boolalpha << std::get<bool>(value);
break;
}
}
std::string typeName() const {
switch (type) {
case Type::INT: return "int";
case Type::DOUBLE: return "double";
case Type::STRING: return "string";
case Type::BOOL: return "bool";
default: return "unknown";
}
}
};
// 方法2:使用可区分联合
struct DiscriminatedUnion {
enum { INT, DOUBLE, STRING, BOOL } tag;
union {
int int_value;
double double_value;
char* string_value; // 注意:需要手动管理内存
bool bool_value;
};
// 构造函数
DiscriminatedUnion(int v) : tag(INT), int_value(v) {}
DiscriminatedUnion(double v) : tag(DOUBLE), double_value(v) {}
DiscriminatedUnion(const char* v) : tag(STRING) {
string_value = new char[strlen(v) + 1];
strcpy(string_value, v);
}
DiscriminatedUnion(bool v) : tag(BOOL), bool_value(v) {}
// 需要手动管理拷贝和析构
~DiscriminatedUnion() {
if (tag == STRING) {
delete[] string_value;
}
}
};
int main() {
std::vector<Data> data_list;
data_list.emplace_back(42);
data_list.emplace_back(3.14159);
data_list.emplace_back("Hello World");
data_list.emplace_back(true);
std::cout << "使用结构体:" << std::endl;
for (const auto& data : data_list) {
std::cout << "类型: " << data.typeName() << ", 值: ";
data.print();
std::cout << std::endl;
}
return 0;
}
5. 比较各种方法的优缺点
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| std::any | 1. 可存储任意类型 2. 类型安全 3. 无需预定义类型集合 | 1. 性能开销较大 2. 需要运行时类型检查 3. 类型信息可能丢失 | 需要最大灵活性的场景,类型不确定 |
| std::variant | 1. 类型安全 2. 编译时类型检查 3. 性能较好 4. 无需动态内存分配 | 1. 需要预知所有可能类型 2. 类型集合固定 | 类型集合已知且有限的场景 |
| 继承多态 | 1. 面向对象设计 2. 支持运行时多态 3. 可扩展性强 | 1. 动态内存分配开销 2. 虚函数调用开销 3. 需要继承层次 | 需要复杂行为多态的场景 |
| 结构体枚举 | 1. 简单直接 2. 性能最好 | 1. 手动类型检查 2. 容易出错 3. 维护困难 | 简单场景,性能敏感 |
6. 综合示例:JSON-like数据结构
cpp
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <variant>
#include <memory>
class JsonValue {
public:
using Object = std::map<std::string, JsonValue>;
using Array = std::vector<JsonValue>;
private:
using Value = std::variant<
std::nullptr_t, // null
bool, // boolean
int, // integer
double, // number
std::string, // string
Array, // array
std::shared_ptr<Object> // object
>;
Value value_;
public:
// 各种构造函数
JsonValue() : value_(nullptr) {}
JsonValue(bool b) : value_(b) {}
JsonValue(int i) : value_(i) {}
JsonValue(double d) : value_(d) {}
JsonValue(const char* s) : value_(std::string(s)) {}
JsonValue(const std::string& s) : value_(s) {}
JsonValue(const Array& arr) : value_(arr) {}
JsonValue(const Object& obj) : value_(std::make_shared<Object>(obj)) {}
// 类型检查
bool isNull() const { return std::holds_alternative<std::nullptr_t>(value_); }
bool isBool() const { return std::holds_alternative<bool>(value_); }
bool isInt() const { return std::holds_alternative<int>(value_); }
bool isDouble() const { return std::holds_alternative<double>(value_); }
bool isNumber() const { return isInt() || isDouble(); }
bool isString() const { return std::holds_alternative<std::string>(value_); }
bool isArray() const { return std::holds_alternative<Array>(value_); }
bool isObject() const { return std::holds_alternative<std::shared_ptr<Object>>(value_); }
// 获取值(不安全,需先检查类型)
bool asBool() const { return std::get<bool>(value_); }
int asInt() const { return std::get<int>(value_); }
double asDouble() const {
if (isInt()) return static_cast<double>(asInt());
return std::get<double>(value_);
}
std::string asString() const { return std::get<std::string>(value_); }
Array& asArray() { return std::get<Array>(value_); }
const Array& asArray() const { return std::get<Array>(value_); }
Object& asObject() { return *std::get<std::shared_ptr<Object>>(value_); }
const Object& asObject() const { return *std::get<std::shared_ptr<Object>>(value_); }
// 安全获取值
template<typename T>
std::optional<T> get() const {
if (auto ptr = std::get_if<T>(&value_)) {
return *ptr;
}
return std::nullopt;
}
// 打印
void print(int indent = 0) const {
std::string indent_str(indent, ' ');
if (isNull()) {
std::cout << "null";
} else if (isBool()) {
std::cout << std::boolalpha << asBool();
} else if (isInt()) {
std::cout << asInt();
} else if (isDouble()) {
std::cout << asDouble();
} else if (isString()) {
std::cout << '"' << asString() << '"';
} else if (isArray()) {
std::cout << "[\n";
const auto& arr = asArray();
for (size_t i = 0; i < arr.size(); ++i) {
std::cout << indent_str << " ";
arr[i].print(indent + 2);
if (i != arr.size() - 1) std::cout << ",";
std::cout << "\n";
}
std::cout << indent_str << "]";
} else if (isObject()) {
std::cout << "{\n";
const auto& obj = asObject();
size_t i = 0;
for (const auto& [key, val] : obj) {
std::cout << indent_str << " \"" << key << "\": ";
val.print(indent + 2);
if (++i != obj.size()) std::cout << ",";
std::cout << "\n";
}
std::cout << indent_str << "}";
}
}
};
int main() {
// 创建复杂的JSON-like结构
JsonValue obj = JsonValue::Object{
{"name", "John"},
{"age", 30},
{"isStudent", false},
{"grades", JsonValue::Array{85, 92, 78}},
{"address", JsonValue::Object{
{"street", "123 Main St"},
{"city", "New York"}
}},
{"nullValue", JsonValue()}
};
std::cout << "JSON-like结构:\n";
obj.print();
std::cout << std::endl;
return 0;
}
总结
现代C++提供了多种方式来处理异构数据集合:
std::any:最灵活,适合需要最大动态性的场景std::variant:类型安全且高效,适合已知类型集合的场景- 继承多态:适合需要复杂行为多态和扩展性的场景
- 结构体+枚举:适合简单场景或性能敏感的场景
选择哪种方法取决于具体需求:
- 如果需要完全动态类型(类似Python),用
std::any - 如果类型集合已知且有限,用
std::variant - 如果需要复杂的行为多态,用继承
- 如果追求极致性能且类型简单,用结构体
现代C++(C++17/20)的这些特性使得处理异构数据变得更加安全和方便,虽然语法比Python复杂,但提供了更好的类型安全和性能保证。