本文将分享Python类成员初始化的详细内容,并且还将对python类的成员进行详尽解释,此外,我们还将为大家带来关于c–从类成员初始化器抛出的异常是否调用std::terminate()?、c–如何
本文将分享Python类成员初始化的详细内容,并且还将对python 类的成员进行详尽解释,此外,我们还将为大家带来关于c – 从类成员初始化器抛出的异常是否调用std :: terminate()?、c – 如何使初始化器列表和类成员初始化程序一起工作?、c# 类成员初始化顺序的特殊情况、C#类成员初始化顺序的相关知识,希望对你有所帮助。
本文目录一览:- Python类成员初始化(python 类的成员)
- c – 从类成员初始化器抛出的异常是否调用std :: terminate()?
- c – 如何使初始化器列表和类成员初始化程序一起工作?
- c# 类成员初始化顺序的特殊情况
- C#类成员初始化顺序
Python类成员初始化(python 类的成员)
我最近刚与Python中的一个错误作斗争。那是那些愚蠢的新手错误之一,但是它让我思考了Python的机制(我是C
++的老程序员,是Python的新手)。我将列出错误的代码并解释如何解决该问题,然后我有两个问题。
场景:我有一个叫做A的类,它有一个字典数据成员,下面是其代码(当然这是简化的):
class A: dict1={} def add_stuff_to_1(self, k, v): self.dict1[k]=v def print_stuff(self): print(self.dict1)
使用此代码的类为B类:
class B: def do_something_with_a1(self): a_instance = A() a_instance.print_stuff() a_instance.add_stuff_to_1(''a'', 1) a_instance.add_stuff_to_1(''b'', 2) a_instance.print_stuff() def do_something_with_a2(self): a_instance = A() a_instance.print_stuff() a_instance.add_stuff_to_1(''c'', 1) a_instance.add_stuff_to_1(''d'', 2) a_instance.print_stuff() def do_something_with_a3(self): a_instance = A() a_instance.print_stuff() a_instance.add_stuff_to_1(''e'', 1) a_instance.add_stuff_to_1(''f'', 2) a_instance.print_stuff() def __init__(self): self.do_something_with_a1() print("---") self.do_something_with_a2() print("---") self.do_something_with_a3()
请注意,每次调用都会do_something_with_aX()
初始化类A的新“干净”实例,并在添加前后打印字典。
错误(如果您尚未弄清楚):
>>> b_instance = B(){}{''a'': 1, ''b'': 2}---{''a'': 1, ''b'': 2}{''a'': 1, ''c'': 1, ''b'': 2, ''d'': 2}---{''a'': 1, ''c'': 1, ''b'': 2, ''d'': 2}{''a'': 1, ''c'': 1, ''b'': 2, ''e'': 1, ''d'': 2, ''f'': 2}
在类A的第二次初始化中,字典不是空的,而是从最后一次初始化的内容开始,依此类推。我希望他们开始“新鲜”。
解决此“错误”的原因显然是添加了:
self.dict1 = {}
在__init__
类A的构造函数中。但是,这使我感到奇怪:
- 在dict1的声明时(类A的第一行),“ dict1 = {}”初始化的含义是什么?没有意义吗?
- 导致从上次初始化复制引用的实例化机制是什么?
- 如果在构造函数(或任何其他数据成员)中添加“ self.dict1 = {}”,它如何不影响先前初始化实例的字典成员?
编辑:按照答案,我现在了解到,通过声明一个数据成员,而不是在__init__
self.dict1或其他地方引用它,我实际上是在C ++ /
Java中定义一个静态数据成员。通过将其称为self.dict1,我将其设为“实例绑定”。
答案1
小编典典您一直称其为错误的是Python类的已记录的标准行为。
__init__
像最初那样在外部声明dict就是在声明类级变量。最初它只会创建一次,每当您创建新对象时,它都会重复使用此相同的字典。要创建实例变量,请使用self
in声明它们__init__
;就这么简单。
c – 从类成员初始化器抛出的异常是否调用std :: terminate()?
struct A { A(int e) { throw e; } }; struct B { A a{42}; // Same with = 42; Syntax }; int main() { try { B b; } catch (int const e) { return e; } }
当编译GCC(版本4.7.4,4.8.5,4.9.3,5.4.0,6.3.0)时:
$g++ -std=c++11 test.cpp -o test; ./test ; echo $? terminate called after throwing an instance of 'int' Aborted 134
但是当用Clang(版本4.0.0)编译时:
$clang++ -std=c++11 test.cpp -o test; ./test ; echo $? 42
哪个行为是正确的?
解决方法
如果构造函数是try / catch子句中的第一个操作,那么编译器认为它不在其中,尽管它应该包含它.
例如,以下工作很好:
#include <iostream> struct A { A(int e) { throw e; } }; struct B { A a{42}; // Same with = 42; Syntax }; int main() { try { // The following forces the compiler to put B's contructor inside the try/catch. std::cout << "Welcome" << std::endl; B b; } catch (int e) { std::cout << "ERROR: " << e << std::endl; // This is just for debugging } return 0; }
运行:
g++ -std=c++11 test.cpp -DNDEBUG -o test; ./test ; echo $?
输出:
Welcome ERROR: 42 0
我的猜测是,由于编译器优化,它将构造函数移动到主函数的开头.它假定struct B没有构造函数,那么它假定它不会抛出异常,因此可以将它移动到try / catch子句之外.
如果我们将struct struct的声明改为明确地使用struct A构造函数:
struct B { B():a(42) {} A a; };
那么结果将如预期的那样,我们将进入try / catch,即使删除“欢迎”打印输出:
ERROR: 42 0
c – 如何使初始化器列表和类成员初始化程序一起工作?
struct X { int x = 1; int y = 1;};int main() { X x = {1,2};}
gcc编译错误:
a.cpp: In function 'int main()':a.cpp:7:16: error: Could not convert '{1,2}' from '<brace-enclosed initializer list>' to 'X'
解决方法
解决方法是添加一个双参数构造函数.这允许您使用initializer-list初始化,即使您的类在技术上不是聚合,也允许与聚合初始化相同的语法.
struct X { X(int x,int y) : x(x),y(y) {} int x = 1; int y = 1;};int main() { X x1{1,2}; X x2 = {1,2};}
注意:这些规则已经为C 1y放宽,这意味着您的类型确实是一个聚合.
c# 类成员初始化顺序的特殊情况
这里直接给出C#类成员一般初始化顺序:
- 子类静态字段
- 子类静态构造
- 子类实例字段
- 父类静态字段
- 父类静态构造
- 父类实例字段
- 父类实例构造
- 子类实例构造
为什么说是“一般”初始化顺序呢?因为根据类结构的不同,类成员的初始化顺序并不是一成不变的。但是这个顺序是基础,可以推导出其他特殊的初始化顺序。下面我们就来看两种特殊的情况:
static void Main(string[] args) { Console.WriteLine("---------------一般初始化顺序---------------"); var child1 = new Child1(); Console.WriteLine("\n---------------子类静态字段初始化需要使用父类静态字段时初始化顺序---------------"); var child2 = new Child2(); Console.WriteLine("\n---------------子类静态构造函数中使用父类静态字段时初始化顺序---------------"); var child3 = new Child3(); Console.ReadKey(); } public class Child1 : Base1 { public static Display ChildStatic = new Display("Child static filed"); private Display _childFiled = new Display("Child filed"); static Child1() => Console.WriteLine("Child static ctor"); public Child1() => Console.WriteLine("Child ctor"); } public class Child2 : Base2 { /// <summary> /// 子类静态字段初始化需要使用父类静态字段 /// </summary> public static Display ChildStatic = new Display("Child static filed", () => BaseStatic); private Display _childFiled = new Display("Child filed"); static Child2() => Console.WriteLine("Child static ctor"); public Child2() => Console.WriteLine("Child ctor"); } public class Child3 : Base3 { public static Display ChildStatic = new Display("Child static filed"); private Display _childFiled = new Display("Child filed"); /// <summary> /// 子类静态构造函数中使用父类静态字段 /// </summary> static Child3() { Console.WriteLine("Child static ctor"); var baseStatic = BaseStatic; } public Child3() => Console.WriteLine("Child ctor"); } /// <summary> /// 3个Base类相同,这里是为了演示静态成员的初始化 /// </summary> public class Base1 { public static Display BaseStatic = new Display("Base static filed"); private Display _baseFiled = new Display("Base filed"); static Base1() => Console.WriteLine("Base static ctor"); public Base1() => Console.WriteLine("Base ctor"); } public class Base2 { public static Display BaseStatic = new Display("Base static filed"); private Display _baseFiled = new Display("Base filed"); static Base2() => Console.WriteLine("Base static ctor"); public Base2() => Console.WriteLine("Base ctor"); } public class Base3 { public static Display BaseStatic = new Display("Base static filed"); private Display _baseFiled = new Display("Base filed"); static Base3() => Console.WriteLine("Base static ctor"); public Base3() => Console.WriteLine("Base ctor"); } public class Display { public Display(string msg, Func<Display> displayFunc = null) { Console.WriteLine(msg); var display = displayFunc?.Invoke(); } }
补充一下:
1. 静态构造函数是线程安全的,会在初次访问该类所定义的其他方法、属性或变量之前执行
2. 编译器会在每个构造函数(包括静态和实例)的开头放入适当的程序码,以便把你在定义成员字段时所指定的初始值设置给这些变量,这就是字段总是在构造函数执行前初始化的原因。
3. 无论是静态变量还是实例变量,其取值都应该在声明的时候得以初始化。但以下3种情况不应该编写初始化语句
- 把字段初始化为0或null。因为系统在执行开发者编写的代码之前,就会把内存清零,重复执行清零指令就显得多余了
- 字段的初始值需要根据不同的构造函数来设定。这种情况下字段的初始化放在构造函数中就可以了,否则会导致创建多余的对象
- 字段的初始化过程中可能出现异常。这种也应该放在构造函数中进行初始化,同时要注意千万不能让静态构造函数中的异常脱出,因为一个AppDomain仅能调用一次某个类的静态构造函数
通过了解类成员的初始化顺序,可以让我们更加详细地了解程序执行的细节,避免写出类似“在构造函数中调用虚函数或抽象函数”的代码。
以上就是c# 类成员初始化顺序的特殊情况的详细内容,更多关于C# 类初始化顺序的资料请关注其它相关文章!
- C#类的创建与初始化实例解析
- C#中序列化实现深拷贝,实现DataGridView初始化刷新的方法
- 初始化的两种方法" target="_blank">C#控制台基础 list<>初始化的两种方法
C#类成员初始化顺序
这里直接给出C#类成员一般初始化顺序:
- 子类静态字段
- 子类静态构造
- 子类实例字段
- 父类静态字段
- 父类静态构造
- 父类实例字段
- 父类实例构造
- 子类实例构造
为什么说是“一般”初始化顺序呢?因为根据类结构的不同,类成员的初始化顺序并不是一成不变的。但是这个顺序是基础,可以推导出其他特殊的初始化顺序。下面我们就来看两种特殊的情况:
static void Main(string[] args) { Console.WriteLine("---------------一般初始化顺序---------------"); var child1 = new Child1(); Console.WriteLine("\n---------------子类静态字段初始化需要使用父类静态字段时初始化顺序---------------"); var child2 = new Child2(); Console.WriteLine("\n---------------子类静态构造函数中使用父类静态字段时初始化顺序---------------"); var child3 = new Child3(); Console.ReadKey(); } public class Child1 : Base1 { public static display ChildStatic = new display("Child static filed"); private display _childFiled = new display("Child filed"); static Child1() => Console.WriteLine("Child static ctor"); public Child1() => Console.WriteLine("Child ctor"); } public class Child2 : Base2 { /// <summary> /// 子类静态字段初始化需要使用父类静态字段 /// </summary> public static display ChildStatic = new display("Child static filed",() => BaseStatic); private display _childFiled = new display("Child filed"); static Child2() => Console.WriteLine("Child static ctor"); public Child2() => Console.WriteLine("Child ctor"); } public class Child3 : Base3 { public static display ChildStatic = new display("Child static filed"); private display _childFiled = new display("Child filed"); /// <summary> /// 子类静态构造函数中使用父类静态字段 /// </summary> static Child3() { Console.WriteLine("Child static ctor"); var baseStatic = BaseStatic; } public Child3() => Console.WriteLine("Child ctor"); } /// <summary> /// 3个Base类相同,这里是为了演示静态成员的初始化 /// </summary> public class Base1 { public static display BaseStatic = new display("Base static filed"); private display _baseFiled = new display("Base filed"); static Base1() => Console.WriteLine("Base static ctor"); public Base1() => Console.WriteLine("Base ctor"); } public class Base2 { public static display BaseStatic = new display("Base static filed"); private display _baseFiled = new display("Base filed"); static Base2() => Console.WriteLine("Base static ctor"); public Base2() => Console.WriteLine("Base ctor"); } public class Base3 { public static display BaseStatic = new display("Base static filed"); private display _baseFiled = new display("Base filed"); static Base3() => Console.WriteLine("Base static ctor"); public Base3() => Console.WriteLine("Base ctor"); } public class display { public display(string msg,Func<display> displayFunc = null) { Console.WriteLine(msg); var display = displayFunc?.Invoke(); } }
补充一下:
- 静态构造函数是线程安全的,会在初次访问该类所定义的其他方法、属性或变量之前执行
- 编译器会在每个构造函数(包括静态和实例)的开头放入适当的程序码,以便把你在定义成员字段时所指定的初始值设置给这些变量,这就是字段总是在构造函数执行前初始化的原因。
- 无论是静态变量还是实例变量,其取值都应该在声明的时候得以初始化。但以下3种情况不应该编写初始化语句
- 把字段初始化为0或null。因为系统在执行开发者编写的代码之前,就会把内存清零,重复执行清零指令就显得多余了
- 字段的初始值需要根据不同的构造函数来设定。这种情况下字段的初始化放在构造函数中就可以了,否则会导致创建多余的对象
- 字段的初始化过程中可能出现异常。这种也应该放在构造函数中进行初始化,同时要注意千万不能让静态构造函数中的异常脱出,因为一个
AppDomain
仅能调用一次某个类的静态构造函数
通过了解类成员的初始化顺序,可以让我们更加详细地了解程序执行的细节,避免写出类似“在构造函数中调用虚函数或抽象函数”的代码。
关于Python类成员初始化和python 类的成员的介绍现已完结,谢谢您的耐心阅读,如果想了解更多关于c – 从类成员初始化器抛出的异常是否调用std :: terminate()?、c – 如何使初始化器列表和类成员初始化程序一起工作?、c# 类成员初始化顺序的特殊情况、C#类成员初始化顺序的相关知识,请在本站寻找。
本文标签: