-
在开始之前,首先声明本篇文章参考[官方文档](https://www.mindspore.cn/docs/programming_guide/zh-CN/r1.3/pipeline_common.html#通用数据处理),我基于官网的这篇文章加以自己的理解发表了这篇博客,希望大家能够更快更简单直观的体验MindSpore,如有不妥的地方欢迎大家指正。 ## 概述 在计算机视觉任务中,数据量过小或是样本场景单一等问题都会影响模型的训练效果,用户可以通过数据增强操作对图像进行预处理,从而提升模型的泛化性。MindSpore提供了内置的`c_transforms`模块用于对图像数据的处理和增强。它是基于C++的OpenCV实现,具有较高的性能,所以在处理图像数据集时,会优先考虑使用该模块的内置算子。 c_transforms模块中常见的数据增强算子如下表所示: | 模块 | 算子 | 说明 | | ------------ | -------------------- | ---------------------------------- | | c_transforms | RandomCrop | 在图像随机位置裁剪指定大小子图像。 | | | RandomHorizontalFlip | 按照指定概率对图像进行水平翻转。 | | | Resize | 将图像缩放到指定大小。 | | | Invert | 将图像进行反相。 | 接下来,我将给大家详细介绍这几个常用算子。 ## RandomCrop ### 1.功能 在随机位置裁剪输入图像。如果输入图像大小小于输出大小,则在剪切前将**填充输入图像**。该算子在数据集偏小的时候非常有用,它可以在有限的样本基础上,有效地扩充数据集。 ### 2、参数 RandomCrop(size, padding=None, pad_if_needed=False, fill_value=0, padding_mode=Border.CONSTANT) * **size:**(int) 裁剪图像的输出大小。如果size是整数,则返回(size, size)的正方形状裁剪。如果大小是长度为2的序列,则应该是(高度,宽度)。 * **padding:**填充图像的像素数(默认值为None),因为图像实际上就是以一个个像素点的形式存储在计算机中的。如果padding不是None,则首先使用padding值填充图像。如果提供了单个数字,请使用此值填充所有边框。如果提供了两个值的元组或列表,则用第一个值填充(左和上),用第二个值填充(右和下)。如果以列表或元组形式提供4个值,则分别填充左、上、右和下。 * **pad_if_needed:**如果任一侧小于给定的输出大小(默认值=False),请填充图像。 * **fill_value:**准备填充边框的像素强度,但仅对填充模式Border.CONSTANT有效。如果是三元组,则分别用于填充R、G、B通道。如果是整数,则用于所有RGB通道。填充值必须在[0255]范围内(默认值为0)。 * **padding_mode:**填充方法(默认值=Border.CONSTANT)。它可以是[Border.CONSTANT、Border.EDGE、Border.REFLECT、Border.SYMMETRIC]中的任意一个(这些都是定义好的常量对象)。 Border.CONSTANT:表示用常量值填充边框。 Border.EDGE:表示它与边上的最后一个值填充。 Border.REFLECT:表示它反映边上的值,忽略边的最后一个值。 Border.SYMMETRIC:表示它反映了重复边的最后一个值的边上的值。 ## RandomHorizontalFlip ### 1.功能 以给定的概率水平随机翻转输入图像。c_transforms模块还提供了一个RandomHorizontalFlipWithBBox算子,该算子不仅可以以给定的概率水平随机翻转输入图像,还可以并相应调整边界框。它们同样是可以在有限的样本基础上,有效地扩充数据集。 ### 2.参数 **prob:**(float)默认值为0.5,图像被翻转的概率。 ## Resize ### 1.功能 使用给定的插值模式以及相应形状将输入图像调整为给定大小。该算子在深度学习中起着非常重要的作用,因为很多经典的神经网路对于输入都有着特定的形状要求,而实际上数据集中的样本形状大小又不满足该网络。这个时候我们就需要使用Resize来讲数据集中的样本图像缩放到指定大小。就比如我们在手写数字识别初体验中使用到的LeNet-5网络,它要求图像的输入是32 x 32,而MNIST数据集中图像的大小又是28 x 28,这个时候我们就需要将Resize算子通过map算子映射到MNIST数据集上。 ### 2.参数 * **size:**调整后图像的输出大小,如果size为一个整数,则图像的较小边将以相同的图像纵横比调整为此值。如果大小是长度2的列表,则应为(高度、宽度)。size一般情况下都是长度为2的列表。 * **interpolation** :图像的插值模式,默认值为Inter.LINEAR。它可以是[Inter.LINEAR、Inter.NEAREST、Inter.BICUBIC、Inter.PILCUBIC]中的任意一种。 - Border.CONSTANT, means it fills the border with constant values. - Border.EDGE, means it pads with the last value on the edge. - Border.REFLECT, means it reflects the values on the edge omitting the last value of edge. - Border.SYMMETRIC, means it reflects the values on the edge repeating the last value of edge. 同理,一般情况下采样默认值即可。 ## Invert ### 1、功能 在RGB模式下对输入图像应用反转。这个操作符将重新分配每个像素到(255像素)。该算子是用来加强数据集的。 ## Rescale ### 1.功能 使用给定的重缩放(rescale)和移位重缩放(shift)输入图像。此运算符将使用以下命令重新缩放输入图像:输出(output)=图像(image)*重新缩放(rescale)+移位(shift)。 ### 2.参数 * **rescale:**重缩放因子 * **shift:**位移因子 如果读者在本篇文章中没有看到自己想要使用的算子或者是觉得写的不够全面,读者可通过下面的链接https://www.mindspore.cn/docs/api/zh-CN/r1.5/api_python/mindspore.dataset.vision.html#mindspore-dataset-vision-c-transforms前往官方API查看更多算子的使用。
-
# 对rec_partition.cc代码分析和注释 本文又开始了新的一篇代码rec_partition.cc的分析与注释,这篇文章是相关部分函数的思路分析和注释。 ## 函数分析和注释 ### GetWeights() 函数功能是获取目标节点的权重以进行排序。 ``` double GetWeights(const Graph::NodeType &node) { const OperatorRec &op = node.apply; if (op.op_type == OperatorType::kRecMatMul) { // For MatMul auto cost_ptr = std::make_shared<CostMatMul>(); return cost_ptr->GetMinCostIn(op); } else if (op.op_type == OperatorType::kRecConvolution) { // For Convolution auto cost_ptr = std::make_shared<CostConvolution>(); return cost_ptr->GetMinCostIn(node); } else if (op.op_type == OperatorType::kRecPooling) { // For Pooling auto cost_ptr = std::make_shared<CostPooling>(); return cost_ptr->GetMinCostIn(); } else if (op.op_type == OperatorType::kRecElmWiseOp) { // For TensorAdd auto cost_ptr = std::make_shared<CostTensorAdd>(); return cost_ptr->GetMinCostIn(); } else if (op.op_type == OperatorType::kRecReLU) { // For Activation auto cost_ptr = std::make_shared<CostCommon>(); return cost_ptr->GetMinCostIn(); } else if (op.op_type == OperatorType::kRecReshape) { // For Reshape auto cost_ptr = std::make_shared<CostReshape>(); return cost_ptr->GetMinCostIn(); } else if (op.op_type == OperatorType::kRecBiasAdd) { // For BiasAdd auto cost_ptr = std::make_shared<CostBiasAdd>(); return cost_ptr->GetMinCostIn(); } else if (op.op_type == OperatorType::kRecLog || op.op_type == OperatorType::kRecExp || op.op_type == OperatorType::kRecAdd || op.op_type == OperatorType::kRecSub || op.op_type == OperatorType::kRecMul || op.op_type == OperatorType::kRecDiv || op.op_type == OperatorType::kRecSqueeze || op.op_type == OperatorType::kRecCast) { // For element-wise op auto cost_ptr = std::make_shared<CostCommon>(); return cost_ptr->GetMinCostIn(); } else if (op.op_type == OperatorType::kRecBatchNorm || op.op_type == OperatorType::kRecOneHot || op.op_type == OperatorType::kRecPReLU || op.op_type == OperatorType::kRecUnsortedSegmentOp || op.op_type == OperatorType::kRecSoftmax || op.op_type == OperatorType::kRecSparseSoftmaxCrossEntropyWithLogits || op.op_type == OperatorType::kRecSoftmaxCrossEntropyWithLogits) { // For BatchParallel op auto cost_ptr = std::make_shared<CostBatchParallel>(); return cost_ptr->GetMaxCostIn(); } else if (op.op_type == OperatorType::kRecUnkownType) { // For Unkown type return 0.0; } else { MS_LOG(EXCEPTION) << "Failure: GetOperatorWeight failed."; //失败:获取运算符重量失败。 } } ``` ### SortByWeight() 函数功能是按权重对所有节点进行排序。 ``` std::vector<size_t> SortByWeight(const std::shared_ptr<Graph> &graph) { MS_EXCEPTION_IF_NULL(graph); std::vector<std::pair<double, size_t>> weight_to_node_index; std::vector<size_t> node_index_by_weights; // 获得节点权重 for (size_t i = 0; i < graph->nodes.size(); i++) { if (graph->nodes<i>.info == kApplication) { const Graph::NodeType &node_ptr = graph->nodes<i>; double weight = GetWeights(node_ptr); size_t index = i; weight_to_node_index.push_back(std::make_pair(weight, index)); } } // 图的运算符节点排序 std::sort(weight_to_node_index.begin(), weight_to_node_index.end()); // Store the result in node_index_by_weights. uint64_t size = weight_to_node_index.size(); for (uint64_t i = 1; i <= size; i++) { node_index_by_weights.push_back(weight_to_node_index[size - i].second); } return node_index_by_weights; } ``` ### PartitionNode() 函数功能是获取目标节点的最优划分策略。 ``` StrategyRec PartitionNode(const Graph::NodeType &node, const std::vector<std::pair<std::string, StrategyRec>> &node_name_to_strategy, const std::shared_ptr<Graph> &graph) { bool enable_conv_chw_partition = false; MS_EXCEPTION_IF_NULL(graph); if (node.apply.op_type == OperatorType::kRecMatMul) { // For MatMul auto cost_ptr = std::make_shared<CostMatMul>(); return cost_ptr->GetOptimalStr(node, node_name_to_strategy, *graph); } else if (node.apply.op_type == OperatorType::kRecConvolution) { // For Convolution auto cost_ptr = std::make_shared<CostConvolution>(); return cost_ptr->GetOptimalStr(node, node_name_to_strategy, *graph, enable_conv_chw_partition); } else if (node.apply.op_type == OperatorType::kRecPooling) { // For Pooling auto cost_ptr = std::make_shared<CostPooling>(); return cost_ptr->GetOptimalStr(node, node_name_to_strategy, *graph); } else if (node.apply.op_type == OperatorType::kRecElmWiseOp) { // For TensorAdd auto cost_ptr = std::make_shared<CostTensorAdd>(); return cost_ptr->GetOptimalStr(node, node_name_to_strategy, *graph); } else if (node.apply.op_type == OperatorType::kRecReLU) { // For Activation auto cost_ptr = std::make_shared<CostCommon>(); return cost_ptr->GetOptimalStr(node, node_name_to_strategy, *graph); } else if (node.apply.op_type == OperatorType::kRecReshape) { // For Reshape auto cost_ptr = std::make_shared<CostReshape>(); return cost_ptr->GetOptimalStr(node); } else if (node.apply.op_type == OperatorType::kRecBiasAdd) { // For BiasAdd auto cost_ptr = std::make_shared<CostBiasAdd>(); return cost_ptr->GetOptimalStr(node, node_name_to_strategy, *graph); } else if (node.apply.op_type == OperatorType::kRecLog || node.apply.op_type == OperatorType::kRecExp || node.apply.op_type == OperatorType::kRecAdd || node.apply.op_type == OperatorType::kRecSub || node.apply.op_type == OperatorType::kRecMul || node.apply.op_type == OperatorType::kRecDiv || node.apply.op_type == OperatorType::kRecSqueeze || node.apply.op_type == OperatorType::kRecCast) { // For element-wise op auto cost_ptr = std::make_shared<CostCommon>(); return cost_ptr->GetOptimalStr(node, node_name_to_strategy, *graph); } else if (node.apply.op_type == OperatorType::kRecBatchNorm || node.apply.op_type == OperatorType::kRecOneHot || node.apply.op_type == OperatorType::kRecPReLU || node.apply.op_type == kRecSoftmax || node.apply.op_type == OperatorType::kRecSparseSoftmaxCrossEntropyWithLogits || node.apply.op_type == kRecUnsortedSegmentOp) { // For BatchParallel type auto cost_ptr = std::make_shared<CostBatchParallel>(); return cost_ptr->GetOptimalStr(node); } else if (node.apply.op_type == OperatorType::kRecSoftmaxCrossEntropyWithLogits) { // For SoftmaxCrossEntropyWithLogits type auto cost_ptr = std::make_shared<CostSoftmaxCrossEntropyWithLogits>(); return cost_ptr->GetOptimalStr(node); } else if (node.apply.op_type == OperatorType::kRecUnkownType) { // For Unkown type StrategyRec default_strategy; return default_strategy; } else { MS_LOG(EXCEPTION) << "Failure: Partition Operator failed."; //失败:分区运算符失败。 } } ``` ### PartitionForAllDevices() 函数功能是对所有设备进行发区。首先对设备数量进行判断: 1、设备数量小于1,则设备数量无法被分区 2、设备数量大于1024,则输出相关信息,设备数量超过最大值 判断完设备数量后,计算iter次数且iter次数不能大于10,再进入n分割循环,相关过程注释中已有。最后进行设备内存控制的判断,返回布尔值。 ``` Status PartitionForAllDevices(const size_t num_device, const double device_memory, const std::shared_ptr<Graph> &graph) { if (num_device < 1) {//设备数量小于1 MS_LOG(EXCEPTION) << "ERROR: Number of devices can't be " << num_device << "."; //错误:设备数量无法被分区。 } if (num_device > 1024) {//设备数量大于1024 MS_LOG(EXCEPTION) << "ERROR: Number of devices can't be larger than 1024."; //错误:设备数不能大于1024。 } MS_EXCEPTION_IF_NULL(graph); // 计算iter次数 int64_t iter_times = static_cast<int64_t>(log2(num_device)); if (iter_times > 10) {//iter次数大于10 MS_LOG(EXCEPTION) << "ERROR: Number of iter_times can't be larger than 10."; } // N切割循环 for (int64_t loop = 0; loop < iter_times; loop++) { // 按权重排序 std::vector<size_t> reorder_node_list = SortByWeight(graph); // 获取节点总数 size_t iter_nodes = reorder_node_list.size(); // 将节点名称映射到其策略的临时向量。 std::vector<std::pair<std::string, StrategyRec>> node_name_to_strategy; // 循环所有节点 for (size_t i_node = 0; i_node < iter_nodes; i_node++) { //获取当前节点的索引 size_t index = reorder_node_list[i_node]; Graph::NodeType &node_ptr = graph->nodes[index]; // 选择最优策略来切割此运算符。并将结果存储在图形中。 graph->nodes[index].apply.str = PartitionNode(node_ptr, node_name_to_strategy, graph); // 将OP策略应用于张量策略。 graph->nodes[index] = ApplyStrToTensor(node_ptr); // 记下该循环中的节点名称及其策略。 auto node_name_to_str = std::pair<std::string, StrategyRec>(graph->nodes[index].name, graph->nodes[index].apply.str); node_name_to_strategy.push_back(node_name_to_str); } } if (DevicesMemoryControl(num_device, device_memory, graph) != SUCCESS) { return FAILED; } else { return SUCCESS; } } ``` ### ApplyStrToTensor() 函数功能是将OP策略应用于张量策略 ``` Graph::NodeType ApplyStrToTensor(Graph::NodeType Node) { // 设置张量parm Node.tensor_parm.tensor_str.str_n = Node.apply.str.outputTensor.str_n; Node.tensor_parm.tensor_str.str_c = Node.apply.str.outputTensor.str_c; Node.tensor_parm.tensor_str.str_h = Node.apply.str.outputTensor.str_h; Node.tensor_parm.tensor_str.str_w = Node.apply.str.outputTensor.str_w; // 设置输入张量的tersor参数 for (int64_t i = 0; i < 2; i++) { Node.apply.arguments<i>.tensor_str.str_n = Node.apply.str.inputTensor<i>.str_n; Node.apply.arguments<i>.tensor_str.str_c = Node.apply.str.inputTensor<i>.str_c; Node.apply.arguments<i>.tensor_str.str_h = Node.apply.str.inputTensor<i>.str_h; Node.apply.arguments<i>.tensor_str.str_w = Node.apply.str.inputTensor<i>.str_w; } return Node;//返回节点 } ``` ### DevicesMemoryControl() 函数功能是设备内存控制,先判断设备数量,若设备数量为0,输出相关信息。然后遍历节点计算已使用内存,循环结束后,判断设备内存是否大于(已用内存/设备数量),若大于则返回success,若小于则返回failed. ``` Status DevicesMemoryControl(const size_t num_device, const double device_memory, const std::shared_ptr<Graph> &graph) { MS_EXCEPTION_IF_NULL(graph); if (num_device == 0) {//设备数量为0 MS_LOG(EXCEPTION) << "Failure: device number is 0.";//失败:设备数量为0。 } uint64_t iter_nodes = graph->nodes.size(); double used_memory = 0.0;//已使用内存 //遍历节点计算已使用内存 for (uint64_t i_node = 0; i_node < iter_nodes; i_node++) { if (graph->nodes[i_node].info == 0) { Graph::NodeType &Node = graph->nodes[i_node]; for (int64_t index = 0; index < 2; index++) { used_memory += Node.apply.arguments[index].tensor_str.str_n * Node.apply.arguments[index].tensor_shape.shape_n * Node.apply.arguments[index].tensor_str.str_c * Node.apply.arguments[index].tensor_shape.shape_c * Node.apply.arguments[index].tensor_str.str_h * Node.apply.arguments[index].tensor_shape.shape_h * Node.apply.arguments[index].tensor_str.str_w * Node.apply.arguments[index].tensor_shape.shape_w * GetDataTypeSize(Node.apply.arguments[index].tensor_type); } } } if (device_memory < (used_memory / num_device)) {//设备内存<(已用内存/设备数量) MS_LOG(EXCEPTION) << "Failure: Out of memory!";//失败:内存不足! return FAILED; } else { return SUCCESS; } } ``` ### GetDataTypeSize() 函数功能是获取数据类型大小,输入指定类型,通过指定函数,输出字节数大小。 ``` size_t GetDataTypeSize(const TensorType &type) { switch (type) { case kInt8: return sizeof(int64_t); case kFloat16: return sizeof(float) / 2; case kFloat32: return sizeof(float); case kDouble64: return sizeof(double); default: MS_LOG(EXCEPTION) "GetDataTypeSize Failed. Unexpected type";//获取数据类型大小失败。意外类型 } ```
-
方法基本语法基本语法 // 方法定义 public static 方法返回值 方法名称(参数类型 形参){ 方法体代码; return 返回值; } public static void main(String[] args) { // 方法调用 返回值变量 = 方法名称 (实参); }看到这里可能有点抽象,接下来写一个具体的方法:两个整数相加public class TestDemo { // 方法定义 public static int Add(int x,int y){ int sum = x+ y; return sum; } public static void main(String[] args) { //方法的调用 Add(10,20); System.out.println(Add(10,20)); }}注意事项1.方法定义时, 参数可以没有。每个参数要指定类型2.方法定义时, 返回值也可以没有, 如果没有返回值, 则返回值类型应写成 void3.方法定义时的参数称为形式参数(形参),方法调用时的参数称为实际参数(实参)4.方法的定义必须在类之中, 代码书写在调用位置的上方或者下方都可以5.所有程序的入口:main函数形参与实参的关系首先我们写一个交换两个数的方法,并运行一下public class Test1 { public static void swap(int a,int b){ int temp = a; a = b; b = temp; } public static void main(String[] args) { int a = 10; int b = 20; System.out.println("交换实参前:"+a+" "+b); swap(a,b); System.out.println("交换实参后:"+a+" "+b); }}为什么没有发生任何改变呢?因为我们交换的是形参,而不是交换的实参。如果要交换实参,我们应该拿到a和b的地址,但是!a和b在main函数中,函数里的变量属于局部变量,存放在栈上。但是在Java中,拿不到栈上的地址,所以a和b的实际值并没有发生改变。如果要交换a和b的值,只能把a和b的值放在堆上(放在堆上的都是对象!!)
-
private,意思为私有的,它是用来修饰成员变量和成员方法的this,修饰的变量用于指代成员变量,其主要作用是(区分局部变量和成员变量的重名问题)方法的形参如果与成员变量同名,不带this修饰的变量指的是形参,而不是成员变量方法的形参没有与成员变量同名,不带this修饰的变量指的是成员变量从单词意思上就知道被private修饰的成员,只能在本类进行访问那么针对private修饰的成员变量,如果需要被其他类使用,则提供相应的操作:1)提供“get变量名()”方法,用于获取成员变量的值,方法用public修饰2)提供“set变量名(参数)”方法,用于设置成员变量的值,方法用public修饰示例代码:public class Animal { private String name; private int age; 提供get/set方法 public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } //成员方法 public void show() { System.out.println(name+","+age); }public class Test { public static void main(String[] args) { //创建对象 Animal a = new Animal(); //给成员变量赋值 a.setName("喵喵"); a.setAge(20); //调用show方法 a.show(); }}
-
继承(inheritance)机制是面向对象程序设计使代码可以复用的重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称为派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,而继承便是类设计层次的复用。例如,以下代码中Student类和Teacher类就继承了Person类。//父类class Person{public: void Print() { cout << "name:" << _name << endl; cout << "age:" << _age << endl; }protected: string _name = "张三"; //姓名 int _age = 18; //年龄};//子类class Student : public Person{protected: int _stuid; //学号};//子类class Teacher : public Person{protected: int _jobid; //工号};
-
1、算术运算符基本四则运算符 + - * / % ,规则比较简单 , 值得注意的是除法 :a) int / int 结果还是 int , 需要使用 double 来计算 .b) 0 不能作为除数c) % 表示取余 , 不仅仅可以对 int 求模 , 也能对 double 来求模.2.关系运算符关系运算符主要有六个 :== != < > <= >=int a = 10;int b = 20;System.out.println(a == b);System.out.println(a != b);System.out.println(a < b);System.out.println(a > b);System.out.println(a <= b);System.out.println(a >= b); 注意 : 关系运算符的表达式返回值都是 boolean 类型 .3.逻辑运算符逻辑运算符主要有三个 :&& || !注意 : 逻辑运算符的操作数 ( 操作数往往是关系运算符的结果 ) 和返回值都是 boolean逻辑与 &&规则 : 两个操作数都为 true, 结果为 true, 否则结果为 false.int a = 10; int b = 20; int c = 30; System.out.println(a < b && b < c);逻辑或 ||规则 : 两个操作数都为 false, 结果为 false, 否则结果为 trueint a = 10;int b = 20;int c = 30;System.out.println(a < b || b < c); 逻辑非 !规则 : 操作数为 true, 结果为 false; 操作数为 false, 结果为 true( 这是个单目运算符 , 只有一个操作数 ).int a = 10;int b = 20;System.out.println(!a < b); 短路求值&& 和 || 遵守短路求值的规则 .System.out.println(10 > 20 && 10 / 0 == 0); // 打印 falseSystem.out.println(10 < 20 || 10 / 0 == 0); // 打印 true 我们都知道 , 计算 10 / 0 会导致程序抛出异常 . 但是上面的代码却能正常运行 , 说明 10 / 0 并没有真正被求值 .结论 :1. 对于 && , 如果左侧表达式值为 false, 则表达式的整体的值一定是 false, 无需计算右侧表达式 .2. 对于 ||, 如果左侧表达式值为 true, 则表达式的整体的值一定是 true, 无需计算右侧表达式 .
-
1、函数的优点:(1)复用代码(2)隐藏实现细节(3)提高可维护性(4)提高可读性便于调试2、函数的创建:def 函数名([输入参数])函数体[return xxx]3、函数的参数传递:(1)函数调用时的参数传递:位置实参:根据形参对应的位置进行实参传递,位置对应例如def fuc1(a,b),调用时fuc1(10,20)关键字实参:根据形参名称进行实参传递,例如def fuc1(a,b),调用时fuc1(b=10,a=20)(2)如果是不可变对象,在函数体内的修改不会影响实参的值如果是可变对象,在函数体内的修改会影响实参的值4、函数的返回值:(1)函数返回多个值时,结果为元组(2)函数返回一个值时,结果为原值(3)函数没有返回值时,省略return5、函数的参数定义:(1)函数定义默认值参数:函数定义时,给形参设置默认值,只有与默认值不符的时候才需要传递实参。只传一个参数,未被定义的参数未默认值。默认值参数注意一定要放在其他需要传递实参的形参最后。(2)个数可变的位置参数:使用*定义,结果为一个元组,例如def fun1(*args)个数可变的关键字形参:使用**定义,结果为一个字典,例如def fun1(**args)如果要让函数接受不同类型的实参,必须在函数定义中将接纳任意数量实参的形参放在最后。 Python先匹配位置实参和关键字实参,再将余下的实参都收集到最后一个形参中。
-
三元运算符是 if-else 语句的快捷方式,也称为条件运算符。[on_true] if [expression] else [on_false]以下是一些示例,您可以使用它们使代码紧凑简洁。下面的语句与它的意思相同,即“如果 y 为 9,则将 10 分配给 x,否则将 20 分配给 x ”。如果需要,我们可以扩展运算符的链接。x = 10 if (y == 9) else 20同样,我们可以对类对象做同样的事情。x = (classA if y == 1 else classB)(param1, param2)在上面的例子中,classA 和 classB 是两个类,其中一个类构造函数将被调用。下面是一个没有的例子。加入评估最小数字的条件。def small(a, b, c): return a if a <= b and a <= c else (b if b <= a and b <= c else c) print(small(1, 0, 1))print(small(1, 2, 2))print(small(2, 2, 3))print(small(5, 4, 3))#Output#0 #1 #2 #3我们甚至可以在列表推导式中使用三元运算符。[m**2 if m > 10 else m**4 for m in range(50)]#=> [0, 1, 16, 81, 256, 625, 1296, 2401, 4096, 6561, 10000, 121, 144, 169, 196, 225, 256, 289, 324, 361, 400, 441, 484, 529, 576, 625, 676, 729, 784, 841, 900, 961, 1024, 1089, 1156, 1225, 1296, 1369, 1444, 1521, 1600, 1681, 1764, 1849, 1936, 2025, 2116, 2209, 2304, 2401]
-
1.Java基本数据类型与表达式,分支循环。 2.String和StringBuffer的使用、正则表达式。 3.面向对象的抽象,封装,继承,多态,类与对象,对象初始化和回收;构造函数、this关键字、方法和方法的参数传递过程、static关键字、内部类,Java的垃极回收机制,Javadoc介绍。 4.对象实例化过程、方法的覆盖、final关键字、抽象类、接口、继承的优点和缺点剖析;对象的多态性:子类和父类之间的转换、抽象类和接口在多态中的应用、多态带来的好处。 5.Java异常处理,异常的机制原理。 6.常用的设计模式:Singleton、Template、Strategy模式。 7.JavaAPI介绍:种基本数据类型包装类,System和Runtime类,Date和DateFomat类等。 8.Java集合介绍:Collection、Set、List、ArrayList、Vector、LinkedList、Hashset、 Tre eSet、Map、HashMap、TreeMap、Iterator、Enumeration等常用集合类API。 9.Java I/O输入输出流:File和FileRandomAccess类,字节流InputStream和OutputStream,字符流Reader和Writer,以及相应实现类,IO性能分析,字节和字符的转化流,包装流的概念,以及常用包装类,计算机编码。 10.Java高级特性:反射、代理和泛型。 11.多线程原理:如何在程序中创建多线程(Thread、Runnable),线程安全问题,线程的同步,线程之间的通讯、死锁。 12.Socket网络编程。
-
PHP 中的字符串变量字符串变量用于包含有字符的值。在创建字符串之后,我们就可以对它进行操作了。您可以直接在函数中使用字符串,或者把它存储在变量中。在下面的实例中,我们创建一个名为 txt 的字符串变量,并赋值为 "Hello world!" 。然后我们输出 txt 变量的值:实例<?php$txt="Hello world!";echo $txt;?>lamp 注释:当您赋一个文本值给变量时,请记得给文本值加上单引号或者双引号。现在,让我们来看看一些常用的操作字符串的函数和运算符。PHP 并置运算符在 PHP 中,只有一个字符串运算符。并置运算符 (.) 用于把两个字符串值连接起来。下面的实例演示了如何将两个字符串变量连接在一起:实例<?php$txt1="Hello world!";$txt2="What a nice day!";echo $txt1 . " " . $txt2;?>上面的代码将输出:Hello world! What a nice day!提示:在上面的代码中,我们已经使用了两次并置运算符。这是由于我们需要在两个字符串之间插入一个空格。PHP strlen() 函数有时知道字符串值的长度是很有用的。strlen() 函数返回字符串的长度(字节数)。下面的实例返回字符串 "Hello world!" 的长度:实例<?phpecho strlen("Hello world!");?>上面的代码将输出:12提示:strlen() 常常用在循环和其他函数中,因为那时确定字符串何时结束是很重要的。(例如,在循环中,我们需要在字符串中的最后一个字符之后结束循环。)PHP strpos() 函数strpos() 函数用于在字符串内查找一个字符或一段指定的文本。如果在字符串中找到匹配,该函数会返回第一个匹配的字符位置。如果未找到匹配,则返回 FALSE。下面的实例在字符串 "Hello world!" 中查找文本 "world":实例<?phpecho strpos("Hello world!","world");?>上面的代码将输出:6提示:在上面的实例中,字符串 "world" 的位置是 6。之所以是 6 而不是 7 的原因是,字符串中第一个字符的位置是 0,而不是 1。
-
1. 面向过程面向过程也是一种编程思想,这种思想,强调凡事要亲力亲为,每一步都是自己来做2.面向对象Java是一门面向对象的语言面向对象OOP也是一种编程思想,这种思想,强调的是结果,我不在意是怎么完成的,我在意的是有对象可以帮我干活比如:我们想吃饭,不在意是哪个厨师做的,也不在意是哪个骑手送的,只要有厨师做,有骑手派送就好了行为习惯 思维方式 比如衣服,没有办法给出一个明确的定义,但是,只要我们看到任何一件衣服,我们就自动把它归类到衣服这个分类中,因为你在过去的生活经验中已经见过太多的衣服,积累经验3.类类是指类型Type,用Java中的关键字class来描述类是一类事物进行抽象总结出来的特征与行为所以,我们可以把类看做是一类事物的模板/设计图纸,一类事物具有相同属性和行为的集合注意:类在现实世界中不是真实存在,它只是一种对象的数据类型4.对象对象就是根据类创建出来的一个个独立且具体的实例对象具有各种特征,并且每个对象的每个特征都可以有自己特定的值对象具有各种行为,每个对象可以执行的操作一个类可以创建出多个对象,对象直接互不影响,我们通过对象唯一的地址值区分对象5.类与对象的关系类是对象的抽象,对象是类的实体Java是一门面向对象的语言,我们的各种操作,都离不开对象,所以,想干活,先创建对象6. 对象创建过程分析比如:Phone p = new Phone();这句话在内存中发生了什么?可以在堆内存中开辟一块空间用来存放对象这个对象需要进行初始化初始化完毕以后就会为这个对象生成一个唯一的地址值在栈内存中开辟一块空间用来存放引用类型Phone类型的变量p将堆内存中对象的地址值交给引用类型的变量p来保存后续就可以根据p中保存的地址,找到堆中的对象,并对对象做操作比如p.brand=“HUAWEI”;就是根据p中保存的地址,找到对象,并对对象的属性赋值7. 封装封装可以提高程序的安全性封装可以让资源按照我们预先规定的方式来操作7.1 封装属性用private修饰属性提供这个属性对应的getXxx()获取值与setXxx()设置值外界就可以调用公共的get与set方法操作属性了注意:访问控制符private修饰的资源只能在本类中使用7.2 封装方法用private修饰方法我们可以在本类的公共方法里调用这个私有的方法外界就可以调用这个公共方法来执行私有方法的功能
-
如果构造函数只接受一个实参,则它实际上定义了转换为此类类型的隐式转换机制。这种构造函数又叫转换构造函数(converting constructor)。能通过一个实参调用的构造函数定义了一条从构造函数的参数类型向类类型隐式转换的规则。只允许 一步类类型转换抑制构造函数定义的隐式转换将构造函数声明为 explicit加以阻止explicit关键字只允许出现在类内的构造函数声明处(只对一个实参的构造函数有效)explicit构造函数只能用于直接初始化,不能将explicit构造函数用于拷贝形式的初始化过程。Sales_data item1(null_book); //正确,直接初始化Sales_data item2 = null_book; //错误:不能将 explicit 构造函数用于拷贝形式的初始化过程尽管编译器不会将explicit的构造函数用于隐式转换过程,但是我们可以使用这样的构造函数 显式地强制进行转换。
-
函数调用时实参将值传入给形参值传递时。如果形参发生改变,并不会影响实参#include <iostream>using namespace std;void change(int num1, int num2){ cout << "交换前" << endl; cout << "num1=" << num1 << endl; cout << "num2=" << num2 << endl; int temp = num1; num1 = num2; num2 = temp; cout << "交换后" << endl; cout << "num1=" << num1 << endl; cout << "num2=" << num2 << endl;}int main(){ int a = 10; int b = 20; change(a, b); cout << "实参的值" << endl; cout << a << endl; cout << b << endl; return 0;}
-
面向过程:是分析解决问题的步骤,然后用函数把这些步骤一步一步地实现,然后在使用的时候一一调用则可。性能较高,所以单片机、嵌入式开发等一般采用面向过程开发面向对象:是把构成问题的事务分解成各个对象,而建立对象的目的也不是为了完成一个个步骤,而是为了描述某个事物在解决整个问题的过程中所发生的行为。面向对象有封装、继承、多态的特性,所以易维护、易复用、易扩展。可以设计出低耦合的系统。 但是性能上来说,比面向过程要低。
上滑加载中
推荐直播
-
探秘仓颉编程语言:华为开发者空间的创新利器
2025/02/22 周六 15:00-16:30
华为云讲师团
本期直播将与您一起探秘颉编程语言上线华为开发者空间后,显著提升开发效率,在智能化开发支持、全场景跨平台适配能力、工具链与生态完备性、语言简洁与高性能特性等方面展现出的独特优势。直播看点: 1.java转仓颉的小工具 2.仓颉动画三方库lottie 3.开发者空间介绍及如何在空间用仓颉编程语言开发
即将直播 -
大模型Prompt工程深度实践
2025/02/24 周一 16:00-17:30
盖伦 华为云学堂技术讲师
如何让大模型精准理解开发需求并生成可靠输出?本期直播聚焦大模型Prompt工程核心技术:理解大模型推理基础原理,关键采样参数定义,提示词撰写关键策略及Prompt工程技巧分享。
去报名 -
华为云 x DeepSeek:AI驱动云上应用创新
2025/02/26 周三 16:00-18:00
华为云 AI专家大咖团
在 AI 技术飞速发展之际,DeepSeek 备受关注。它凭借哪些技术与理念脱颖而出?华为云与 DeepSeek 合作,将如何重塑产品与应用模式,助力企业数字化转型?在华为开发者空间,怎样高效部署 DeepSeek,搭建专属服务器?基于华为云平台,又该如何挖掘 DeepSeek 潜力,实现智能化升级?本期直播围绕DeepSeek在云上的应用案例,与DTSE布道师们一起探讨如何利用AI 驱动云上应用创新。
去报名
热门标签