首先从属性、方法、staticmethod、property总体上来把握:
属性:
类属性:分公有的、私有的类属性。一般的都是公有的。为了灵活,比如隐藏类属性,就有了私有的属性。
实例属性:(不需要在类中显示定义,是实例对象所特有的)
方法:
类方法:原则上,类方法是将类本身作为对象进行操作的方法(用修饰器"@classmethod"来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以"cls"作为第一个参数),实例对象和类对象都可以调用。
实例方法(以实例对象作为其第一个参数,一般写作'self'),只能由实例对象调用。
举个例子:双均线的交易策略中,两根均线的值是固定的,不同的品种使用这个策略,双均线的值是不变的,那他们就应该定义成类属性,而且在类外面也要用到,所以是公有的。方法的话,肯定每个实例都要用到的,所以必须是实例的方法。
静态方法:一个独立的、单纯的函数。在静态方法中,不会涉及到类中的属性和方法的操作(也不能使用)。实例对象和类对象都可以调用。
property:就是属性,将一个方法变成属性调用,所以在那个方法前加上@xxxx.setter,然后再搞一个同的函数,前面加上@property,直接返回一个值就可以了。具体见文末示例中的@salary.setter。
那么如何区别类方法和静态方法呢?可以参看这里。
先记住下面这个例子:
class Circle(object):
"""docstring for Circle"""
PI = 3.14
__r = 1.0
def __init__(self, r=1):
self.__r = r
#声明设置半径的方法
def setRadius(self,r):
self.__r = r
#声明获取半径的方法
#不需要把属性暴露出去
def getRadius(self):
return self.__r
def Area(self):
return self.PI*self.__r*self.__r
以及自己的一个练习
另外,python类中的函数要先定义,才能调用,有先后顺序,可以参考这里
类
类是对现实世界中一些事物的封装,定义一个类可以采用下面的方式来定义:
class ClassName:
block
类名约定以大写开头(函数名是以小写开头),注意类名后面有个冒号。
类对象
当一个类定义完之后,就产生了一个类对象。类对象支持两种操作:引用和实例化。引用操作是通过类对象去调用类中的属性或者方法,而实例化是产生出一个类对象的实例,称作实例对象。比如定义了一个people类:
class People:
name = 'jack' #定义了一个属性
#定义了一个方法
def printName(self):
print self.name
People类定义完成之后就产生了一个全局的类对象,可以通过类对象来访问类中的属性和方法了。
当通过people.name来访问时,people.name中的people称为类对象。
实例对象
当然还可以进行实例化操作,p=People( ),这样就产生了一个People的实例对象,此时也可以通过实例对象p来访问属性或者方法了(p.name).
属性、方法和函数
要说到属性,方法,我觉得还是下面这张图最能说清楚。比如将下面这个乌龟当作对象,我们如何来描述它呢?我们可以分成两部分来说:一是外观特征,二是从动态的一方面来描述。
我们将静态的特征称之为属性,将动态的动作称之为方法。
在上面代码中注释的很清楚了,name是一个属性,printName( )是一个方法。
一般在类里面定义的函数与类对象或者实例对象绑定了,所以称作为方法;而在类外定义的函数一般没有同对象进行绑定,就称为函数。
(1)私有属性
要定义私有属性,则需在前面加2个下划线 ' __'。

提示找不到该属性,因为私有属性是不能够在类外通过对象名来进行访问的。
(2)方法
在类中可以根据需要定义一些方法,定义方法采用def关键字,在类中定义的方法至少会有一个参数,一般以名为'self'的变量作为该参数(用其他名称也可以),而且需要作为第一个参数。
(3)类属性、实例属性、类方法、实例方法以及静态方法
A: 先来谈一下类属性和实例属性
在前面的例子中我们接触到的就是类属性,顾名思义,类属性就是类对象所拥有的属性,它被所有类对象的实例对象所共有,在内存中只存在一个副本。对于公有的类属性,在类外可以通过类对象和实例对象访问。
class people:
name = 'jack' #公有的类属性
__age = 12 #私有的类属性
p = people()
print p.name #正确
print people.name #正确
print p.__age #错误,不能在类外通过实例对象访问私有的类属性
print people.__age #错误,不能在类外通过类对象访问私有的类属性
属性或者方法私有: 在外部不能直接使用, 只能通过内部成员使用,这时需要增加get、set方法。
实例属性是不需要在类中显示定义的,比如:
class people:
name = 'jack'
p = people()
p.age =12
print p.name #正确
print p.age #正确
print people.name #正确
print people.age #错误
结果如下:
在类外对类对象people进行实例化之后,产生了一个实例对象p,然后p.age = 12这句给p添加了一个实例属性age,赋值为12。这个实例属性是实例对象p所特有的,注意,类对象people并不拥有它(所以不能通过类对象来访问这个age属性)。当然还可以在实例化对象的时候给age赋值。
class people:
name = 'jack'
#__init__()是内置的构造方法,在实例化对象时自动调用
def __init__(self,age):
self.age = age
p = people(12) #不加参数也报错
print p.name #正确
print p.age #正确
print people.name #正确
print people.age #错误
如图:
如果需要在类外修改类属性,必须通过类对象去引用然后进行修改。如果通过实例对象去引用,会产生一个同名的实例属性,这种方式修改的是实例属性,不会影响到类属性,并且之后如果通过实例对象去引用该名称的属性,实例属性会强制屏蔽掉类属性,即引用的是实例属性,除非删除了该实例属性。
class people:
country = 'china'
print people.country
p = people()
print p.country
p.country = 'japan'
print p.country #实例属性会屏蔽掉同名的类属性
print people.country
del p.country #删除实例属性
print p.country
结果:
B: 类方法、实例方法和静态方法
下面来看一下类方法、实例方法和静态方法的区别。
类方法:是类对象所拥有的方法,需要用修饰器"@classmethod"来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以"cls"作为第一个参数(当然可以用其他名称的变量作为其第一个参数,但是大部分人都习惯以'cls'作为第一个参数的名字,就最好用'cls'了),能够通过实例对象和类对象去访问。
class people:
country = 'china'
#类方法,用classmethod来进行修饰
@classmethod
def getCountry(cls):
return cls.country
p = people()
print p.getCountry() #可以用过实例对象引用
print people.getCountry() #可以通过类对象引用
类方法还有一个用途就是可以对类属性进行修改:
class people:
country = 'china'
#类方法,用classmethod来进行修饰
@classmethod
def getCountry(cls):
return cls.country
@classmethod
def setCountry(cls,country):
cls.country = country
p = people()
print p.getCountry() #可以用过实例对象引用
print people.getCountry() #可以通过类对象引用
p.setCountry('japan')
print p.getCountry()
print people.getCountry()
运行结果:
结果显示在用类方法对类属性修改之后,通过类对象和实例对象访问都发生了改变。
补充:
有时候,比如我们想简化这个操作,或者想埋下一个彩蛋时,可以在类里面写一个类方法,这个类方法能够返回一个隐藏的实例:
class People:
def __init__(self, name, age, salary):
self.name = name
self.age = age
self.salary = salary
def intro(self):
print(f'我的名字是{self.name},我今年{self.age}岁了,我的月薪为{self.salary}元')
@classmethod
def kingname(cls):
return cls('kingname', 26, 999999999)
当我们要初始化 kingname这个实例的时候,直接使用 People.kingname()即可,运行效果如下图所示:



实例方法:在类中最常定义的成员方法,它至少有一个参数并且必须以实例对象作为其第一个参数,一般以名为'self'的变量作为第一个参数(当然可以以其他名称的变量作为第一个参数)。在类外实例方法只能通过实例对象去调用,不能通过其他方式去调用。
class people:
country = 'china'
#实例方法
def getCountry(self):
return self.country
p = people()
print p.getCountry() #正确,可以用过实例对象引用
print people.getCountry() #错误,不能通过类对象引用实例方法
静态方法:
定义:使用装饰器@staticmethod。参数随意,没有“self”和“cls”参数,但是方法体中不能使用类或实例的任何属性和方法;
调用:实例对象和类对象都可以调用。
静态方法是类中的函数,不需要实例。静态方法主要是用来存放逻辑性的代码,逻辑上属于类,但是和类本身没有关系,也就是说在静态方法中,不会涉及到类中的属性和方法的操作。可以理解为,静态方法是个独立的、单纯的函数,它仅仅托管于某个类的名称空间中,便于使用和维护。
譬如,我想定义一个关于时间操作的类,其中有一个获取当前时间的函数。

如上,使用了静态方法(函数),然而方法体中并没使用(也不能使用)类或实例的属性(或方法)。若要获得当前时间的字符串时,并不一定需要实例化对象,此时对于静态方法而言,所在类更像是一种名称空间。
其实,我们也可以在类外面写一个同样的函数来做这些事,但是这样做就打乱了逻辑关系,也会导致以后代码维护困难。
参考:https://www.cnblogs.com/wcwnina/p/8644892.html
对于类属性和实例属性,如果在类方法中引用某个属性,该属性必定是类属性,而如果在实例方法中引用某个属性(不作更改),并且存在同名的类属性,此时若实例对象有该名称的实例属性,则实例属性会屏蔽类属性,即引用的是实例属性,若实例对象没有该名称的实例属性,则引用的是类属性;
如果在实例方法更改某个属性,并且存在同名的类属性,此时若实例对象有该名称的实例属性,则修改的是实例属性,若实例对象没有该名称的实例属性,则会创建一个同名称的实例属性。想要修改类属性,如果在类外,可以通过类对象修改,如果在类里面,只有在类方法中进行修改。
从类方法和实例方法以及静态方法的定义形式就可以看出来,类方法的第一个参数是类对象cls,那么通过cls引用的必定是类对象的属性和方法;而实例方法的第一个参数是实例对象self,那么通过self引用的可能是类属性、也有可能是实例属性(这个需要具体分析),不过在存在相同名称的类属性和实例属性的情况下,实例属性优先级更高。静态方法中不需要额外定义参数,因此在静态方法中引用类属性的话,必须通过类对象来引用。
参考资料:Python 面向对象编程
九、类的继承
1. 我觉得类的继续讲得最好的。
来源:《我的Python世界》玩
class ScaleConverter:
def __init__(self,units_from,units_to,factor):
self.units_from = units_from
self.units_to = units_to
self.factor = factor
def description(self):
return 'convert'+self.units_from + 'to' + sefl.units_to
def convert(self,value):
return value * self.factor
子类继承
class ScaleAndOffsetConverter(ScaleConverter):
def __init__(self,units_from,units_to,factor,offset):
ScaleConverter.__init__(self,self,units_from,units_to,factor)
self.offset = offset
def convert(self,value):
return value * self.factor + self.offset
使用:
c1 = ScaleConverter("inches","mm",25)
c2 = ScaleAndOffsetConverter("C","F",1.8,32)
2.vnpy中用到的类的继续
class CtpGateway(VtGateway):
"""CTP接口"""
#----------------------------------------------------------------------
def __init__(self, eventEngine, gatewayName='CTP'):
"""Constructor"""
super(CtpGateway, self).__init__(eventEngine, gatewayName)
self.mdApi = CtpMdApi(self) # 行情API
self.tdApi = CtpTdApi(self) # 交易API
self.mdConnected = False # 行情API连接状态,登录完成后为True
self.tdConnected = False # 交易API连接状态
父类是长这样的
class VtGateway(object):
"""交易接口"""
#----------------------------------------------------------------------
def __init__(self, eventEngine, gatewayName):
"""Constructor"""
self.eventEngine = eventEngine
self.gatewayName = gatewayName
十、类的参数
可变参数 *args 和关键字参数**kw
*args是非关键字参数,传入任意个参数不需要包含参数名,用于tuple
**kw是关键字参数,传入任意个参数要带参数名,用于dict
>>> def f(*args, **kw):
... print('args= ',args) #args接收任意个不带参数名的参数
... print('kw= ',kw) #kw接收任意个带参数名的参数
...
>>> f(1,2,3,a=4,b=5,c=6)
args= (1, 2, 3)
kw= {'a': 4, 'b': 5, 'c': 6}
可参考:https://blog.csdn.net/zhu_1997/article/details/88258165
十一、类的传递

类中的函数加了property,执行函数时不用加().
最后几分钟:https://www.bilibili.com/video/av28871471/?p=2
十二、类的Super

十三、str用来美化输出
class Person():
def __init__(self):
self.name = "张三"
if __name__ == '__main__':
a = Person()
print(a)
打印结果:

这个实例化后的结果显示.Person object 显示的内容看不懂,为了美化输出,可以加个__str__方法
class Person():
def __init__(self):
self.name = "张三"
def __str__(self):
return self.name
if __name__ == '__main__':
a = Person()
print(a)
打印结果:张三
十四、函数中包含类
看:C:\vnstudio\Lib\site-packages\vnpy\trader\database\database_sql.py的
def init_models(db: Database, driver: Driver):
class DbBarData(ModelBase):
十五、一个例子
import json
#定义一个字典,存储数据
staff_info = {'worker_id': 0, 'staffs': [], 'department': []}
class Staff:
staff_id = staff_info['worker_id']
def __init__(self):
self.name = ''
self._age = 0
self.job_number = 'python1805'
self._salary = 0
self.position = '员工'
self.department = '流水线'
def __str__(self):
return '员工信息:\n姓名:'+self.name+'\n年龄:%d'%self.age+'\n工号:'+self.job_number+'\n薪资:%d'%self.salary+'\n职位:'+self.position+'\n部门:'+self.department
@classmethod
def load(cls,dic):
staff = cls()
for key in dic:
if key == 'age':
staff.age = dic['age']
elif key == 'salary':
staff.salary = dic['salary']
else:
staff.__setattr__(key,dic[key])
return staff
#Python内置的@property装饰器就是负责把一个方法变成属性调用的
#@property的实现比较复杂,我们先考察如何使用。把一个getter方法变成属性,只需要加上@property就可以了,此时,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值
@property
def age(self):
return self._age
@age.setter
def age(self,value):
while value.isdigit()!=True or int(value) < 15 or int(value) > 100:
value = input('你输入的年龄不合法,请重新输入:')
self._age = int(value)
@property
def salary(self):
return self._salary
@salary.setter
def salary(self, value):
while value.isdigit() != True or int(value) < 0:
value = input('你输入的工资不合法,请重新输入:')
self._salary = int(value)
@classmethod
def add_staff(cls):
staff = cls()
print('新员工入职')
cls.staff_id += 1
staff.name = input('员工姓名:')
staff.age = input('年龄:')
staff.job_number = 'python1805'+str(cls.staff_id).rjust(3,'0')
staff.salary = input('薪资:')
staff.position = input('请输入员工职位:')
while True:
print('请选择部门')
for index in range(len(staff_info['department'])):
print('%d. %s'%(index+1,staff_info['department'][index]))
i = input('请输入部门序号:')
if i.isdigit()==False or int(i)<=0 or int(i)>index+1:
print('请输入正确的序号.')
else:
staff.department = staff_info['department'][int(i)-1]
break
print('添加成功!')
staff_info['staffs'].append(staff)
@classmethod
def del_staff(cls):
job_number = input('请输入要删除的员工号:')
i = 0
for iterm in staff_info['staffs']:
if iterm.job_number == job_number:
print('员工信息:\n',iterm)
staff_info['staffs'].remove(iterm)
print('员工已删除!')
i += 1
break
if i == 0:
print('该员工不存在!')
@staticmethod
def find_staff():
name = input('请输入要查找的员工姓名:')
i = 0
for item in staff_info['staffs']:
if item.name == name:
print('查找结果:')
print(item)
i+=1
if i == 0:
print('该员工不存在!')
@staticmethod
def find_salary():
print('请选择部门:')
while True:
print('请选择部门')
for index in range(len(staff_info['department'])):
print('%d. %s'%(index+1,staff_info['department'][index]))
i = input('请输入部门序号:')
if i.isdigit()==False or int(i)<=0 or int(i)>index+1:
print('请输入正确的序号.')
else:
break
staff = staff_info['staffs'][0]
for item in staff_info['staffs']:
if item.department == staff_info['department'][int(i)-1] and item.salary >staff.salary:
staff = item
print('工资最高的员工信息如下:')
print(staff)
@staticmethod
def avg_age():
age_sum = 0
for item in staff_info['staffs']:
age_sum += item.age
print('公司全体员工的平均年龄为:%.1f'%(age_sum/len(staff_info['staffs'])))
@classmethod #自己试写的,无误
def add_department(cls):
department = input('请输入要添加的部门名称:')
if department in staff_info['department']:
print('该部门已存在!')
return
staff_info['department'].append(department)
return '添加成功!'
# @staticmethod #不需要类实例化的情况下调用方法
# def add_department():
# department = input('请输入要添加的部门名称:')
# if department in staff_info['department']:
# print('该部门已存在!')
# return
# staff_info['department'].append(department)
# return '添加成功!'
try:
with open('./Stafflist.json','r',encoding='utf-8') as file:
info = json.load(file)
for index in range(len(info['staffs'])):
staff_info['staffs'].append(Staff.load(info['staffs'][index]))
staff_info['worker_id'] = info['worker_id']
staff_info['department'] = info['department']
except:
file = open('./Stafflist.json','w')
file.close()
#主菜单函数
def menu():
while True:
print('\t汉思员工管理系统\t'.center(36,'*'))
print('**' + '\t1. 员工入职\t'.center(29,' ')+' **')
print('**' + '\t2. 删除员工\t'.center(29, ' ') + ' **')
print('**' + '\t3. 查找员工\t'.center(29, ' ') + ' **')
print('**' + '\t4. 薪资最高\t'.center(29, ' ') + ' **')
print('**' + '\t5. 平均年龄\t'.center(29, ' ') + ' **')
print('**' + '\t6. 添加部门\t'.center(29, ' ') + ' **')
print('**' + '\t7. 退出系统\t'.center(29, ' ') + ' **')
# center() 返回一个原字符串居中,并使用空格填充至长度 width 的新字符串。默认填充字符为空格。
print('*'*40)
while True:
n = input('请选择要执行的操作:')
if n == '1':
Staff.add_staff()
break
elif n == '2':
Staff.del_staff()
break
elif n == '3':
Staff.find_staff()
input()
break
elif n == '4':
Staff.find_salary()
break
elif n == '5':
Staff.avg_age()
break
elif n == '6':
print(Staff.add_department())
break
elif n == '7':
with open('./Stafflist.json', 'w') as file:
for index in range(len(staff_info['staffs'])):
staff_info['staffs'][index] = staff_info['staffs'][index].__dict__
# staff_info['worker_id']=len(staff_info['staffs'])
json.dump(staff_info,file)
exit()
else:
print('请输入正确的序号!')
if __name__ == '__main__':
menu()
参考:https://www.jianshu.com/p/931a1c068ad4
十六、一个类的好的示例
class People():
def __init__(self,gun):
self.gun = gun
def fire(self):
gun.shoot()
class Gun():
def __init__(self,bulletBox):
self.bulletBox = bulletBox
def shoot(self):
bulletBox.bulletNum -=1
print("子弹剩余%d发" % bulletBox.bulletNum)
if bulletBox.bulletNum == 0:
print("没子弹了,请重新装弹")
class BulletBox():
def __init__(self,bulletNum):
self.bulletNum = bulletNum
bulletNum = int(input("请输入子弹个数:"))
if bulletNum >= 7:
print("只能装填7发子弹")
else:
bulletBox = BulletBox(bulletNum)
gun = Gun(bulletBox)
pro = People(gun)
while 1:
pro.fire()
if bulletBox.bulletNum == 0:
break
另外请留意,
init中一般只定义创建类时就要定义的必须属性,其他非必须属性可以在其他类中定义。
比如有的在类中的函数忽然出现一个self.xxx,接着在同一个类的另一个函数又调用这个self.xxx,这样是可以的。
class Bullitbox():
"""docstring for ClassName"""
def __init__(self, num):
self.num = int(num)
class Gun():
"""docstring for Gun"""
def __init__(self, bullitbox):
self.bullitbox = bullitbox
def fire(self):
if self.bullitbox.num == 0:
print("子弹打光了...................")
elif self.bullitbox.num < 0:
exit()
else:
self.bullitbox.num -=1
print("fire.....")
print("还有{}颗子弹".format(self.bullitbox.num))
self.testnum = 55
def test(self):
self.last = self.testnum + 1
print(self.last)
class Person():
"""docstring for Person"""
def __init__(self, gun):
self.gun = gun
def shoot(self):
self.gun.fire()
self.gun.test()
bbox = Bullitbox("5")
gun = Gun(bbox)
p = Person(gun)
for i in range(20):
p.shoot()
十七、老外教程
(一)将类作为参数传入另一个类中


(二)类的继承
1.要继承name, age就在super后面填name, age.
2.name,age前面不需要加self.

(三)cls method


(三)@staticmethod
they do sth, but they don't change anything.
https://www.youtube.com/watch?v=JeznW_7DlB0








