Home >  > 类,类对象,实例对象,属性,方法和函数

类,类对象,实例对象,属性,方法和函数

0

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

Snap18063

属性、方法和函数

要说到属性,方法,我觉得还是下面这张图最能说清楚。比如将下面这个乌龟当作对象,我们如何来描述它呢?我们可以分成两部分来说:一是外观特征,二是从动态的一方面来描述。

我们将静态的特征称之为属性,将动态的动作称之为方法。

Snap18064

在上面代码中注释的很清楚了,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方法。

Snap18068

实例属性是不需要在类中显示定义的,比如:

class people:
    name = 'jack'

p = people()
p.age =12
print p.name    #正确
print p.age     #正确

print people.name    #正确
print people.age     #错误

结果如下:

Snap18069

在类外对类对象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     #错误

如图:

Snap18070

如果需要在类外修改类属性,必须通过类对象去引用然后进行修改。如果通过实例对象去引用,会产生一个同名的实例属性,这种方式修改的是实例属性,不会影响到类属性,并且之后如果通过实例对象去引用该名称的属性,实例属性会强制屏蔽掉类属性,即引用的是实例属性,除非删除了该实例属性。

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

结果:

Snap18071

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()    #可以通过类对象引用

Snap18072

类方法还有一个用途就是可以对类属性进行修改:

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()    

运行结果:

Snap18073

结果显示在用类方法对类属性修改之后,通过类对象和实例对象访问都发生了改变。

补充:

有时候,比如我们想简化这个操作,或者想埋下一个彩蛋时,可以在类里面写一个类方法,这个类方法能够返回一个隐藏的实例:

    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()    #错误,不能通过类对象引用实例方法

Snap18074

静态方法:
定义:使用装饰器@staticmethod。参数随意,没有“self”和“cls”参数,但是方法体中不能使用类或实例的任何属性和方法;
调用:实例对象和类对象都可以调用。

静态方法是类中的函数,不需要实例。静态方法主要是用来存放逻辑性的代码,逻辑上属于类,但是和类本身没有关系,也就是说在静态方法中,不会涉及到类中的属性和方法的操作。可以理解为,静态方法是个独立的、单纯的函数,它仅仅托管于某个类的名称空间中,便于使用和维护。

譬如,我想定义一个关于时间操作的类,其中有一个获取当前时间的函数。

如上,使用了静态方法(函数),然而方法体中并没使用(也不能使用)类或实例的属性(或方法)。若要获得当前时间的字符串时,并不一定需要实例化对象,此时对于静态方法而言,所在类更像是一种名称空间。

其实,我们也可以在类外面写一个同样的函数来做这些事,但是这样做就打乱了逻辑关系,也会导致以后代码维护困难。

参考:https://www.cnblogs.com/wcwnina/p/8644892.html

对于类属性和实例属性,如果在类方法中引用某个属性,该属性必定是类属性,而如果在实例方法中引用某个属性(不作更改),并且存在同名的类属性,此时若实例对象有该名称的实例属性,则实例属性会屏蔽类属性,即引用的是实例属性,若实例对象没有该名称的实例属性,则引用的是类属性;

如果在实例方法更改某个属性,并且存在同名的类属性,此时若实例对象有该名称的实例属性,则修改的是实例属性,若实例对象没有该名称的实例属性,则会创建一个同名称的实例属性。想要修改类属性,如果在类外,可以通过类对象修改,如果在类里面,只有在类方法中进行修改。

从类方法和实例方法以及静态方法的定义形式就可以看出来,类方法的第一个参数是类对象cls,那么通过cls引用的必定是类对象的属性和方法;而实例方法的第一个参数是实例对象self,那么通过self引用的可能是类属性、也有可能是实例属性(这个需要具体分析),不过在存在相同名称的类属性和实例属性的情况下,实例属性优先级更高。静态方法中不需要额外定义参数,因此在静态方法中引用类属性的话,必须通过类对象来引用。

参考资料:Python 面向对象编程

九、类的继承

1. 我觉得类的继续讲得最好的。
来源:《我的Python世界》玩学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

本文暂无标签

发表评论

*

*