类实例化
python的构造和析构函数为固定的名字,不像c++中那样构造函数和析构函数是类名字。注意所有的类内部的函数都默认有自身self的输入参数。
构造函数--------------------- init( self )
析构函数--------------------- del( self )
不同于C++里面的类,在python中构造函数和析构函数可以省略。看如下的类定义:
class SimpleTest:
def __init__(self):
pass
def __del__(self):
pass
def func(self):
pass
上面定义了一个简单的类,一个构造函数,析构函数和普通的函数。当然在Python中这个类可以简化成:
class SimpleTest:
def func( self ):
pass
析构函数可以用于在构造函数中定义全局变量时,类离开自身作用域时调用析构函数进行内存的释放,增加代码的鲁棒性与稳定性。
下面过一过类的正常实例化的方法:
定义类
python中定义一个类的格式如下:
class MyClass(object):
def __init__(self,data1,data2):
self.__data1=data1
self.data2=data2
def __func1(self):
print("MyClass类的私有方法被调用!")
def print_data(self):
self.__func1()
print(self.__data1)
print(self.data2)
def setData(self,data):
self.__data1=data
class1=MyClass('first_data','sencond_data')
class1.print_data()
class1._MyClass__func1() #强行使用类内的私有函数,不健康的语法调用,减少使用
类通过关键字 class 引导,后跟类的名称,通常第一个字母大写,类名称MyClass后的括号内的object表示该类的父类,如果定义的类没有显明从哪个类继承来的,就在括号内使用object,object类是所有类的父类。
__init__是定义的类的构造函数,可以对类的变量进行初始化,每当该类被实例化的时候,就会先执行该构造函数。
在类中定义的函数的一个参数一定要是self,代表类当前的实例化对象。但在调用函数时并不需要传递这个参数。
关于抽象类:一个抽象基类只能被继承,而其本身无法被实例化。在抽象基类中可定义纯虚函数,又称为抽象方法。这样的方法类似于其他语言中的接口声明,只能够被继承类重写后进行调用,其本身无法被调用。且继承此抽象基类的所有类必须强制实现基类中定义的所有纯虚函数,否则代码将报错。可见,纯虚函数是对当前抽象基类的继承者所进行的一种接口约束,纯虚函数通过abstractmethod装饰器进行声明。
类的实例
类的实例化方法如下:
obj=MyClass(x,x)
obj是类的实例化对象,MyClass是类名,括号内的变量是类的初始化变量(如果类中有定义的话)。
情况一: 类的初始属性为空
class MyClass(object):
def __init__(self):
print("MyClass类的构造方法被调用!")
class1=MyClass()
情况二: 类含有初始属性
class MyClass(object):
def __init__(self,data1,data2):
self.__data1=data1
self.data2=data2
class1=MyClass('first_data','sencond_data')
print(class1.data2)
类的封装
封装、继承和多态是类的三大特征。类的封装有两层含义,一个是对数据的封装,一个是对实现逻辑即方法的封装;通过类封装,python内部类也含有了多态性。
- 数据(属性)的封装
class MyClass(object):
def __init__(self,data1,data2):
self.__data1=data1
self.data2=data2
class1=MyClass('first_data','sencond_data')
print(class1.data2)
在这里类MyClass的实例化对象class1就具有了两个属性,分别是data1和data2,data1是私有属性,只能在类内使用,data2是公有属性,可以在类外使用。data1和data2就是对数据(属性)的封装。
- 实现逻辑(方法)的封装
class MyClass(object):
def __init__(self,data1,data2):
self.__data1=data1
self.data2=data2
def printMax(self):
if self.__data1>self.data2:
print(self.__data1)
else:
print(self.data2)
class1=MyClass(66,88)
class1.printMax()
上边类中的printMax函数实现了比较两个数据大小并打印的功能,这个实现逻辑在类外部来说是看不到的,只可以调用该接口,相当于一个黑箱子,这就是实现逻辑(方法)的封装。
类的私有属性
可以在类内定义类的私有属性和方法,私有的表明只属于类内部的,在类外部是不可以直接访问的,python定义私有属性和私有方法的方法是在名称前加上两个下划线 “__”。
私有属性(变量)
class MyClass(object):
def __init__(self,data1,data2):
self.__data1=data1
self.data2=data2
class1=MyClass(66,88)
print(class1.data2)
print(class1.__data1)
上例中data2是公有变量,可以在类外访问,所有calss1.data2没有问题。 data1是类的私有属性,类外不可以访问,使用 class1.__data1会报错 ‘MyClass’ object has no attribute ‘__data1’ 。
私有方法
class MyClass(object):
def __init__(self,data1,data2):
self.__data1=data1
self.data2=data2
def __func1(self):
print("MyClass类的私有方法被调用!")
def print_data(self):
self.__func1()
print(self.__data1)
print(self.data2)
class1=MyClass(66,88)
class1.print_data()
#class1.__func1()
上例中 print_data方法是类的公有方法,可以在类外直接调用, __func1方法是类的私有方法,不可以在类外调用,但是可以通过类内的print_data方法调用私有的__func1方法,即私有的方法可以在类的内部被调用。
类外访问类的私有属性
类的私有属性保证了外部代码不能随意访问/更改对象内部的数据和方法。
但是对类内私有属性,在类外仍然可以通过以下两种 方式进行访问和修改,一是通过类内的公有函数修改,一是“非法修改”。
通过类内的公有函数修改
class MyClass(object):
def __init__(self,data1,data2):
self.__data1=data1
self.data2=data2
def setData1(self,data):
self.__data1=data
def printData1(self):
print(self.__data1)
class1=MyClass(66,88)
class1.setData1(100)
class1.printData1()
非法修改
python中类的私有属性或方法之所以不能直接从类外部进行访问,是因为python解释器把私有的属性或方法 __xx 对外展示成了 _Class__xx,即单下划线+类名+__xx 。所以如果你执意,仍然可以在类外部通过这个改变后的名称“非法”访问私有属性。
class MyClass(object):
def __init__(self,data1,data2):
self.__data1=data1
self.data2=data2
def printData1(self):
print(self.__data1)
class1=MyClass(66,88)
print(class1._MyClass__data1)
当然一般不建议这么干,不同的python编译器可能把私有属性/方法包装成不同的名字。
以上实例化内容来自于:https://blog.csdn.net/dcrmg/article/details/75041125
跨文件夹import
链接https://codingpy.com/article/python-import-101/ 有全面的关于python文件import介绍,仔细且非常地道。
在跨文件夹导入的工作中,需要在对应的文件夹下添加空白__init__.py文件,这令我想起之前在从事ROS开发的时候在文件夹内添加空白文件.ignore文件,可以在编译的情况下自动跳过该文件夹。跨文件夹导入的文件夹组织形式如下,要注意一般而言我们推荐跨文件夹导入的层级结构不超过两层。
my_package/
__init__.py
subpackage1/
__init__.py
module_x.py
module_y.py
subpackage2/
__init__.py
module_z.py
module_a.py
在查阅pytorch的official tutorial的过程中,发现@property的修饰器语法,涉及到Python语言中的属性,在多方搜索之后对于@property的用法仍然很迷,感觉如同鸡肋。
常见的属性起因于python的动态语言特性,主要针对类而言:
类属性
class Test:
name = "Hello"
name就是类属性,我们可以直接通过类的__dict__里拿到这个值。也可以直接使用Test.name来调用。 Test.dict[“name”],在定义类属性之后,虽然这个属性属于类所有,但是由这个类实例化而来的所有实例均可以访问。
在__init__()外初始化,在内部用classname.类属性名调用,外部既可以用classname.类属性名又可以用instancename.类属性名来调用。
实例属性
实例属性在__init__()运行之后才被确定,内部调用时都需要加上self.,外部调用时用instancename.propertyname
class Test:
name = “Hell”
def init(self, user):
self.user = user
Fuck = Test("fuck")
这里面的self.user就是实例属性,可以通过Fuck.user来调用,也能用Fuck.dict[“user”]来调用。
Attention Point
实际上,上面这一段代码运行后,当我们使用Fuck.name调用属性的时候,会发现值是fuck。此时,实例属性与类属性同名而掩盖住了类属性,导致程序不能直接使用self.name调用类属性。
此时可以使用Fuck.class.dict[“name”]来访问类属性。或者删掉重名的实例属性del Fuck.name
之后,再调用Fuck.name这时我们就能调用到类属性了。然而,此时会发现Fuck.dict[“name”]会报错,提醒你没有这个key。这很好理解,Fuck.__dict__这个字典存储的是Fuck这个实例的属性,而不是Test这个类的。