首先从属性、方法、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