一、类基础
1.初始化类
class Circle(object): # 创建Circle类,Circle为类名
pass # 此处可添加属性和方法
#注意:我们定义的类都会继承于object类,当然也可以不继承object类;
#两者区别不大,但没有继承于object类使用多继承时可能会出现问题。
#有了Circle类的定义,就可以创建出具体的circle1、circle2等实例,
# circle1和circle2是个实际的圆。创建实例使用 类名+(),类似函数调用的形式创建。
circle1 = Circle()
circle2 = Circle()
circle1.r = 1 # r为实例属性
circle2.R = 2.2
print(circle1.r)
print(circle2.R)
'创建个类,然后实例它'
var foo = 'bar';
2.实例属性1
class Circle(object):
#在定义 Circle 类时,可以为 Circle 类添加一个特殊的 __init__() 方法,
# 当创建实例时,__init__() 方法被自动调用为创建的实例增加实例属性。
#我们在此为每个实例都统一加上我们需要的属性(用法类似java的构造方法):
def __init__(self,r):
self.r = r
#相应,创建实例时就必须要提供除 self 以外的参数:
circle1 = Circle(1) # 创建实例时直接给定实例属性,self不算在内
circle2 = Circle(2)
print(circle1.r) # 实例名.属性名 访问属性
print(circle2.r)
3.实例属性2
class Circle(object): # 创建Circle类
def __init__(self,R): # 约定成俗这里应该使用r,它与self.r中的r同名
self.r = R
circle1 = Circle(1)
# print(circle1.R) 错误
print(circle1.r) #我们访问的是小写r
4.类属性
class Circle(object):
pi = 3.14 #类属性
def __init__(self,r):
self.r = r
#绑定在实例上的属性不会影响其他实例,但类本身也是一个对象,
#如果在类上绑定属性,则所有实例都可以访问该类的属性,
# 并且所有实例访问的类属性都是同一个!!!记住,实例属性每个实例各自拥有,
# 互相独立,而类属性有且只有一份。
circle1 = Circle(1)
circle2 = Circle(2)
print('----未修改前----')
print('pi= \t',Circle.pi)
print('circle1.pi= \t',circle1.pi)
print('circle2.pi= \t',circle2.pi)
print('----通过类名修改后----')
Circle.pi = 3.14159 # 通过类名修改类属性,所有实例的类属性被改变
print('pi= \t',Circle.pi)
print('circle1.pi= \t',circle1.pi)
print('circle2.pi= \t',circle2.pi)
circle1.pi = 3.1
circle2.pi = 3.2
print('----通过实例修改----')
print('circle1.pi= \t',circle1.pi)
print('circle2.pi= \t',circle2.pi)
print('----删除circle1实例属性pi----')
del circle1.pi
print('pi= \t',Circle.pi)
print('circle1.pi= \t',circle1.pi)
print('circle2.pi= \t',circle2.pi)
5.实例方法
class Circle(object):
pi = 3.14
def __init__(self,r):
self.r = r
def get_area(self):
# return self.r**2 * Circle.pi #通过实例修改pi的值对面积无影响,这个pi为类属性的值
return self.r ** 2 * self.pi #通过实例修改pi的值对面积我们圆的面积就会改变
circle1 = Circle(0.5)
print(circle1.get_area()) # 调用方法 self不需要传入参数,不要忘记方法后的括号 输出 3.14
circle2 = Circle(1)
print(circle2.get_area())
6.私有属性
class Circle(object):
__pi = 3.14 #私有属性,不能对外部访问
def __init__(self,r):
self.r = r
def area(self):
return self.r ** 2 * self.pi
circle1 = Circle(1)
#print(circle1.pi)
#print(Circle.pi)
#出现异常,访问权限问题
7.私有方法
class Circle(object):
__pi = 3.14
def __init__(self,r):
self.r = r
def area(self):
return self.r ** 2 * self.__pi
def __girth(self):
return self.r * 2 * self.__pi
circle1 = Circle(2)
# print(circle1.__girth())
#私有化方法后,我们只能在类的内部调用该方法,不能在外部调用
8.@classmethod方法1
# python中的@classmethod、@staticmethod 装饰方法
# @classmethod 用来修饰方法。使用在实例化前与类进行交互,但不和其实例进行交互的函数方法上。
# @staticmethod 用来修饰类的静态方法。使用在有些与类相关函数,但不使用该类或该类的实例。如更改环境变量、修改其他类的属性等。
# 两者最明显的区别,classmethod 必须使用类的对象作为第一个参数,而staticmethod则可以不传递任何参数
# 1.@classmethod 类方法
#类方法,我们不用通过实例化类就能访问的方法。而且@classmethod 装饰的方法不能使用实例属性,
# 只能是类属性。它主要使用在和类进行交互,但不和其实例进行交互的函数方法上。
class Circle(object):
__pi = 3.14
def __init__(self,r):
self.r = r
@classmethod
def pi(cls):
return cls.__pi
def area(self):
"""
圆的面积
"""
return self.r ** 2 * self.__pi
print(Circle.pi()) # 没有实例化 能直接访问pi() 方法
circle1 = Circle(2)
print(circle1.pi()) # 也可以通过实例访问pi()方法
# Circle类下的pi()方法被 @classmethod 装饰后,我们能通过Circle.pi() 直接运行方法,不用实例化类。
9.@classmethod方法2
class Date(object):
day = 0
month = 0
year = 0
def __init__(self,year=0,month=0,day=0):
self.year = year
self.month = month
self.day = day
@classmethod
def form_string(cls,date_as_string):
year,month,day = date_as_string.split('-')
date = cls(year,month,day)
return date
date1 = Date.form_string('2017-10-17')
print(date1.year,date1.month,date1.day)
# from_string 返回的是Date类的实例,所以我们可以通过from_string 实例化类。
# 注意:from_string(cls, date_as_string)中cls表示的是类,它和self类实例有一定的差别。
# 类方法中都是使用cls,实例方法中使用self。
10.staticmethod方法
# @staticmethod 和@classmethod非常的相似,
# 但是@staticmethod 不强制要求传递参数(它做的事与类方法或实例方法一样)。
# @staticmethod使用在有些和类相关函数,但不使用该类或者该类的实例。
# 如更改环境变量、修改其他类的属性等。
# 一句话@staticmethod 修饰的方法是放在类外的函数,
# 我们为了方便将他移动到了类里面,它对类的运行无影响。
class Date(object):
day = 0
month = 0
year = 0
def __init__(self,year=0,month=0,day=0):
self.year = year
self.month = month
self.day = day
@classmethod
def from_string(cls,date_as_string):
year,month,day = date_as_string.split('-')
date = cls(year,month,day)
return date
@staticmethod
def is_date_valid(date_as_string):
"""
用来校验日期的格式是否正确
"""
year,month,day = date_as_string.split('-')
return int(year) <= 3999 and int(month) <= 12 and int(day) <=31
date1 = Date.from_string('2017-10-17')
print(date1.year,date1.month,date1.day)
is_date = Date.is_date_valid('2017-10-17')
# 注意:@staticmethod修饰方法 is_date_valid(date_as_string)中无实例化参数self或者cls;
# 而@classmethod修饰的方法中有from_string(cls, date_as_string) 类参数cls。
11.property1
# property 的有两个作用
# 作为装饰器 @property 将类方法转换为类属性(只读)
# property 重新实现一个属性的 setter 和 getter 方法
# 1、@property 将类方法转换为只读属性(常用)
# 使用 property 的最简单的方法是将它作为装饰器来使用。这可以让你将一个类方法转变成一个类属性。
class Circle(object):
__pi = 3.14
def __init__(self,r):
self.r = r
@property
def pi(self):
return self.__pi
circle1 = Circle(2)
print(circle1.pi)
# 上面示例装饰了pi方法,创建实例后我们可以使用circle1.pi 自己获取方法的返回值,而且他只能读不能修改。
12.property2
# 2、property 重新实现 setter 和 getter 方法(少用) 不学了
13.类的继承
# 如下定义一个动物类Animal为基类,它基本两个实例属性name和age、一个方法call。
class Animal(object):
def __init__(self,name,age):
self.name = name
self.age = age
def call(self):
print(self.name,'会叫')
# 现在我们需要定义一个Cat 猫类继承于Animal,猫类比动物类多一个sex属性。
class Cat(Animal):
def __init__(self,name,age,sex):
super(Cat, self).__init__(name,age) # 不要忘记从Animal类引入属性
self.sex = sex
if __name__ == '__mian__': # 单模块被引用时下面代码不会受影响,用于调试
c = Cat('喵喵',2,'公') # Cat继承了父类Animal的属性
c.call() # 输出 喵喵 会叫 ,Cat继承了父类Animal的方法
c = Cat('喵喵',2,'公')
print(c.name,c.age,c.sex)
print(c.call())
# 一定要用 super(Cat, self).__init__(name,age) 去初始化父类,
# 否则,继承自 Animal的 Cat子类将没有 name和age两个属性。
# 函数super(Cat, self)将返回当前类继承的父类,即 Animal,
# 然后调用__init__()方法,注意self参数已在super()中传入,在__init__()中将隐式传递,不能再写出self。
14.子类方法的重构
#上面例子中 Animal 的子类 Cat 继承了父类的属性和方法,但是我们猫类 Cat 有自己的叫声 '喵喵' ,
# 这时我们可以对父类的 Call() 方法进行重构。如下:
class Animal(object):
def __init__(self,name,age):
self.name = name
self.age = age
def call(self):
print(self.name,'会叫')
class Cat(Animal):
def __init__(self,name,age,sex):
super(Cat, self).__init__(name,age)
self.sex = sex
def call(self):
print(self.name,'会’喵喵‘叫')
if __name__ == '__mian__':
c = Cat('喵喵',2,'公')
c.call()
c = Cat('公猫',2,'公')
print(c.name,c.age,c.sex)
print(c.call()) # 输出:喵喵 会“喵喵”叫
# 类方法的调用顺序,当我们在子类中重构父类的方法后,Cat子类的实例先会在自己的类 Cat 中查找该方法,
# 当找不到该方法时才会去父类 Animal 中查找对应的方法。
15.子类与父类的关系
class Animal(object):
pass
class Cat(Animal):
pass
A = Animal()
C = Cat()
# “A”是 Animal 类的实例,但,“A”不是 Cat 类的实例。
# “C”是 Animal 类的实例,“C”也是 Cat 类的实例。
print('"A" is Animal?',isinstance(A,Animal))
print('"A" is Cat?',isinstance(A,Cat))
print('"C" is Animal?',isinstance(C,Animal))
print('"C" is Cat?',isinstance(C,Cat))
# 函数 isinstance() 不止可以用在我们自定义的类,也可以判断一个变量的类型,
# 如判断数据类型是否为 int、str、list、dict 等。
print(isinstance(100,int))
print(isinstance('100',int))
print(isinstance(100,str))
print(isinstance('100',str))
16.多态
# 类具有继承关系,并且子类类型可以向上转型看做父类类型,
# 如果我们从 Animal 派生出 Cat和 Dog,并都写了一个 call() 方法,如下示例:
class Animal(object):
def __init__(self,name,age):
self.name = name
self.age = age
def call(self):
print(self.name,'会叫')
class Cat(Animal):
def __init__(self,name,age,sex):
super(Cat,self).__init__(name,age)
self.sex = sex
def call(self):
print(self.name,'会“喵喵叫')
class Dog(Animal):
def __init__(self,name,age,sex):
super(Dog,self).__init__(name,age)
self.sex = sex
def call(self):
print(self.name,'会“汪汪“叫')
# 我们定义一个 do 函数,接收一个变量 ‘all’,如下:
def do(all):
all.call()
A = Animal('小黑',4)
C = Cat('喵喵',2,'男')
D = Dog('旺财',5,'女')
for x in (A,C,D):
do(x)
# 这种行为称为多态。也就是说,方法调用将作用在 all 的实际类型上。
# C 是 Cat 类型,它实际上拥有自己的 call() 方法以及从 Animal 继承的 call 方法,
# 但调用 C .call() 总是先查找它自身的定义,如果没有定义,则顺着继承链向上查找,直到在某个父类中找到为止。
#传递给函数 do(all) 的参数 all 不一定是 Animal 或 Animal 的子类型。
# 任何数据类型的实例都可以,只要它有一个 call() 的方法即可。
# 其他类不继承于 Animal,具备 call 方法也可以使用 do 函数。
# 这就是动态语言,动态语言调用实例方法,不检查类型,只要方法存在,参数正确,就可以调用。
二、TSN补充学习
0.argparse模块
# argparse的使用可以简化成下面四个步骤
#
# 1:import argparse
#
# 2:parser = argparse.ArgumentParser()
#
# 3:parser.add_argument()
#
# 4:parser.parse_args()
#上面四个步骤解释如下:首先导入该模块;
# 然后创建一个解析对象;
# 然后向该对象中添加你要关注的命令行参数和选项,每一个add_argument方法对应一个你要关注的参数或选项;
# 最后调用parse_args()方法进行解析;解析成功之后即可使用。
1.argparse模块1
# argsparse是python的命令行解析的标准模块,内置于python,
# 不需要安装。这个库可以让我们直接在命令行中就可以向程序中传入参数并让程序运行。
# 传入一个参数
import argparse
parser = argparse.ArgumentParser(description='命令行中传入一个数字')
#type是要传入的参数的数据类型 help是该参数的提示信息
parser.add_argument('integers',type=str,help='传入的数字')
args = parser.parse_args()
#获得传入的参数
print(args)
# 在这个代码中,实现我们在终端传入一个数字。
# 使用方法是打开命令行,先把路径转到py文件所在的文件夹
# cd..返回上级目录,cd 文件夹名 前往文件夹
# cd D:\PyCharm\pycharm_workplace\python_base\python_base\TSN补充学习
# 然后再命令行中输入 python 文件名.py -h 或者 python 文件名.py --help
# 在命令行中看到py文件运行结果如下:
# usage: 1.argparse模块.py [-h] integers
##
## 命令行中传入一个数字
##
## positional arguments:
## integers 传入的数字
##
## optional arguments:
## -h, --help show this help message and exit
# 现在我们在命令行中给 1.argparse模块.py传入一个参数5
# python 1.argparse模块.py 5
# 运行,得到的运行结果是:
# Namespace(integers='5')
# 操作args字典
# 得到的这个结果Namespace(integers='5')是一种类似于python字典的数据类型。
# 我们可以使用 arg.参数名来提取这个参数
print(args.integers)
# 在命令行输完 python 1.argparse模块.py 5后
# 运行,得到的运行结果是:
# Namespace(integers='5')
# 5
2.argparse模块2
# 传入多个参数
# 现在在命令行中给1.argparse模块.py传入多个参数,例如传入1,2,3,4四个数字
# python 1.argparse模块.py 1 2 3 4
# 运行报错:
# usage: 1.argparse模块.py [-h] integers
# 1.argparse模块.py: error: unrecognized arguments: 2 3 4
# 不能识别2 3 4,看源代码我们知道integers这个参数是位置参数,
# 说明第一个数1是能识别。这里我们需要重新更改demo.py代码
import argparse
parser = argparse.ArgumentParser(description='命令行中传入多个数字')
parser.add_argument('integers',type=str,nargs='+',help='传入的数字')
args = parser.parse_args()
print(args.integers)
# nargs是用来说明传入的参数个数,'+' 表示传入至少一个参数。
# 这时候再重新在命令行中运行python 2.argparse模块2.py 1 2 3 4得到:
# ['1', '2', '3', '4']
3.argparse模块3
# 改变数据类型
# add_argument中有type参数可以设置传入参数的数据类型。
# 我们看到代码中有type这个关键词,该关键词可以传入list, str, tuple, set, dict等。
# 例如我们把上面的type=str,改成type=int,这时候我们就可以进行四则运算。
import argparse
parser = argparse.ArgumentParser(description='命令行在传入一个数字')
parser.add_argument('integers',type=int,nargs='+',help='传入的数字')
args = parser.parse_args()
# 对传入的数据进行加总
print(sum(args.integers))
# 在命令行中输入 python 3.argparse模块.py 1 2 3 4, 运行结果为:
# 10
4.argparse模块4
# 位置参数
#在命令行中传入参数时候,传入的参数的先后顺序不同,
# 运行结果往往会不同,这是因为采用了位置参数,例如
import argparse
parser = argparse.ArgumentParser(description='姓名')
parser.add_argument('param1',type=str,help='姓')
parser.add_argument('param2',type=str,help='名')
args = parser.parse_args()
# 打印姓名
print(args.param1 + args.param2)
# 在命令行中分别输入python 4.argparse模块4.py 张 三和python 4.argparse模块4.py 三 张,
# 得到的 运行结果分别为 张三 和 三张
#如果我们将代码parser.add_argument('param1', type=str,help='姓')和
# parser.add_argument('param2', type=str,help='名')互换位置,即第4行和第五行代码,再重新运行
# python 4.argparse模块4.py张 三 和 python 4.argparse模块4.py 三 张,得到的 运行结果分别为
# 三张 和 张三
5.argparse模块5
# 可选参数
# 为了在命令行中避免上述位置参数的bug(容易忘了顺序),可以使用可选参数,
# 这个有点像关键词传参,但是需要在关键词前面加--,例如
import argparse
parser = argparse.ArgumentParser(description='姓名')
parser.add_argument('--family',type=str,help='姓')
parser.add_argument('--name',type=str,help='名')
args = parser.parse_args()
print(args.family + args.name)
# 在命令行中输入
# python python 5.argparse模块.py --family=张 --name=三
# 运行结果
#
# 张三
6.argparse模块6
# 默认值
# add_argument中有一个default参数。有的时候需要对某个参数设置默认值,
# 即如果命令行中没有传入该参数的值,程序使用默认值。
# 如果命令行传入该参数,则程序使用传入的值。具体请看下面的例子
import argparse
parser = argparse.ArgumentParser(description='姓名')
parser.add_argument('--family', type=str, default='张',help='姓')
parser.add_argument('--name', type=str, default='三', help='名')
args = parser.parse_args()
# 打印姓名
print(args.family+args.name)
# 在命令行中分别输入 python 6.argparse模块.py 、 python 6.argparse模块.py --family=李
# 运行结果分别为
# 张三
# 和
# 李三
7.argparse模块7
# 必需参数
# add_argument有一个required参数可以设置该参数是否必需。
import argparse
parser = argparse.ArgumentParser(description='姓名')
parser.add_argument('--family', type=str, help='姓')
parser.add_argument('--name', type=str, required=True, default='', help='名')
args = parser.parse_args()
# 打印姓名
print(args.family+args.name)
# 在命令行中输入 python demo.py --family=张,运行结果
# usage: 7.argparse模块.py [-h] [--family FAMILY] --name NAME
# 7.argparse模块.py: error: the following arguments are required: --name
# 因为可选参数name的required=True,所以必须要传入。
# 如果我们将其更改为False,程序运行结果
# 张
8.if else Python用法
new_length = None
modality = 'optical flow'
if new_length is None:
# 三目运算式,假如new_length=1,如果modality是'RGB',则new_length的值就是1,否则的话就是5
new_length = 1 if modality == "RGB" else 5
else:
new_length = new_length
print(new_length)
9.getattr模块
# getattr(object,name[,default])
# 作用:返回object的名称为name的属性的属性值,
# 如果属性name存在,则直接返回其属性值;
# 如果属性name不存在,则触发AttribetError异常或当可选参数default定义时返回default值。
# 这个方法最主要的作用是实现反射机制。也就是说可以通过字符串获取方法实例。
# 这样,你就可以把一个类可能要调用的方法放在配置文件里,在需要的时候动态加载。
class A(object):
bar = 1
# 获取属性 bar 值
a = A()
getattr(a,'bar')
print(a.bar)
# 属性 bar2 不存在,触发异常
b = A()
# getattr(b,'bar2')
# print(b)
# 属性 bar2 不存在,但设置了默认值
getattr(b,'bar2',3)
print(b.bar2)
# getattr(self.base_model, self.base_model.last_layer_name)
# getattr同样是torch.nn.Module类的一个方法,getattr是获得属性值,
# 一般可以用来获取网络结构相关的信息,以上述语句为例,输入包含2个值,
# 分别是基础网络和要获取值的属性名。
10.setattr模块
# setattr(object, name, value)
class A(object):
bar = 1
a = A()
getattr(a,'bar') # 获取属性 bar 值
print(a.bar)
setattr(a,'bar',5) # 设置属性 bar 值
print(a.bar)
# setattr(self.base_model, self.base_model.last_layer_name, nn.Dropout(p=self.dropout))
# setattr是torch.nn.Module类的一个方法,用来为输入的某个属性赋值,一般可以用来修改网络结构。
# 以上述引用为例,输入包含3个值,分别是基础网络、要赋值的属性名、要赋的值,
# 当这个setattr运行结束后,self.base_model.last_layer_name这一层就是nn.Dropout(p=self.dropout)。