Python 面向对象编程(进阶 I)

类的成员可以分为三大类:字段、方法和属性

所有成员中,只有普通字段的内容保存对象中,即:根据此类创建了多少对象,在内存中就有多少个普通字段。而其他的成员,则都是保存在类中,即:无论对象的多少,在内存中只创建一份

 

 

字段包括:普通字段和静态字段,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同

  • 普通字段属于对象
  • 静态字段属于

应用场景: 通过类创建对象时,如果每个对象都具有相同的字段,那么就使用静态字段

 举个例子,模拟CS游戏场景:

class createRole(object):
    ac=None # 类属性/变量, 静态字段
    Num=0
    def __init__(self,name,role,weapon,lifeValue): 
        self.Name=name # self.Name 成员属性/变量
        self.Role=role
        self.Weapon=weapon
        self.LifeValue=lifeValue
        createRole.Num+=1

    def buyWeapon(self,weapon): #类的方法(普通方法)
        print('%s is buying [%s]' %(self.Name,weapon))
        self.Weapon=weapon

p1=createRole('P1','Police','B10',100)
t1=createRole('T1','Terrorist','B11',100)
p2=createRole('P1','Police','B12',100)
t2=createRole('T2','Terrorist','B13',100)

p1.ac='China-made'
t1.ac='Japan-made'

createRole.ac='US-made'
createRole.Weapon='XD' #创建了一个类中的变量Weapon,并赋值,和实例化后的Weapon变量不同
p1.buyWeapon('AK47') #createRole.buy_weapon(p1,'AK47')
t1.buyWeapon('B51') #和ac一样,buyWeapon并没有在每个类的实例化对象中都创建一遍,所有的实例化对象都共用类中的buyWeapon

print('P1:', p1.Weapon, p1.ac, id(p1.ac))
print('T1:', t1.Weapon, t1.ac, id(t1.ac))
print('P2:', p2.Weapon, p2.ac, id(p2.ac))
print('T2:', t2.Weapon, t2.ac, id(t2.ac))
print(createRole.ac,createRole.Num,id(createRole.ac))

执行结果:
P1 is buying [AK47]
T1 is buying [B51]
P1: AK47 China-made 139965298329840
T1: B51 Japan-made 139965298329904
P2: B12 US-made 139965298317328
T2: B13 US-made 139965298317328
US-made 4 139965298317328
# 可以看到p1.ac,p2.ac语句创建了一个实例变量ac,和类中的ac变量不同

 

方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。

  • 普通方法:至少一个self参数,可以在实例化后直接调用,并且在方法里可以通过self.调用实例变量或类变量。
  • 静态方法:静态方法不可以访问实例变量或类变量,由调用;通过@staticmethod装饰器即可把其装饰的方法变为一个静态方法
  • 类方法:类方法只能访问类变量,不能访问实例变量,由调用;类方法通过@classmethod装饰器实现

静态方法

  修改上个例子加上装饰器如下
  @staticmethod
def buyWeapon(self,weapon): #类的方法(静态方法) print('%s is buying [%s]' %(self.Name,weapon)) self.Weapon=weapon

再次执行,结果如下:
TypeError: buyWeapon() missing 1 required positional argument: 'weapon'

分析:当 buyWeapon 变成静态方法后,再通过实例调用时就不会自动把实例本身当作一个参数传给self了。
有两种解决办法:
1、调用时主动传递实例本身给 buyWeapon 方法
p1.buyWeapon(p1,'AK47')
t1.buyWeapon(t1,'B51')
2、在buyWeapon方法中去掉self参数,但这也意味着,在buyWeapon中不能通过self.调用实例中的其它变量了

类方法

  修改上个例子加上装饰器如下
  @classmethod
    def buyWeapon(self,weapon): #类的方法(类方法)
        print('%s is buying [%s]' %(self.Name,weapon))
        self.Weapon=weapon
再次执行,结果如下:
AttributeError: type object 'createRole' has no attribute 'Name'

分析:name是个实例变量,类方法是不能访问实例变量的,只能访问类变量
  @staticmethod
def buyWeapon(self,weapon): #类的方法(类方法) print(self.ac,weapon) self.Weapon=weapon

再次执行,结果如下:

US-made AK47
US-made B51
P1: B10 China-made 139988741179632
T1: B11 Japan-made 139988741179696
P2: B12 US-made 139988741167120
T2: B13 US-made 139988741167120
US-made 4 139988741167120

对于所有的方法而言,均属于类(非对象)中,所以,在内存中也只保存一份。方法调用者不同、调用方法时自动传入的参数不同。

 

属性

 

属性的作用就是把一个方法变成一个静态属性,其实是普通方法的变种

属性的定义有两种方式:

  • 装饰器 即:在方法上应用装饰器
  • 静态字段 即:在类中定义值为property对象的静态字段

 

装饰器方式:在类的普通方法上应用@property装饰器


class
Foo: def func(self): pass # 定义属性,属性仅有一个self参数 @property def prop(self): pass foo_obj = Foo() foo_obj.func() #调用方法 foo_obj.prop #调用属性

把一个方法变成静态属性有什么用呢?Python的属性的功能是:属性内部进行一系列的逻辑计算,最终将计算结果返回。

比如 ,你想知道一个航班当前的状态,是到达了、延迟了、取消了、还是已经飞走了, 想知道这种状态你必须经历以下几步 :

1. 连接航空公司API查询

2. 对查询结果进行解析 

3. 返回结果给你的用户

因此这个status属性的值是一系列动作后才得到的结果,所以你每次调用时,其实它都要经过一系列的动作才返回你结果,但这些动作过程不需要用户关心, 用户只需要调用这个属性就可以。

class Flight(object):
    def __init__(self,name):
        self.flight_name = name


    def checking_status(self):
        print("checking flight %s status " % self.flight_name)
        return  1

    @property
    def flight_status(self):
        status = self.checking_status()
        if status == 0 :
            print("flight got canceled...")
        elif status == 1 :
            print("flight is arrived...")
        elif status == 2:
            print("flight has departured already...")
        else:
            print("cannot confirm the flight status...,please check later")


f = Flight("CA980")
f.flight_status
Flight

 

经典类中的属性只有一种访问方式,其对应被 @property 修饰的方法
新式类(继承object类)中的属性有三种访问方式,并分别对应了三个被@property、@方法名.setter、@方法名.deleter修饰的方法

由于新式类中具有三种访问方式,我们可以根据他们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除

class Goods(object):

    def __init__(self):
        # 原价
        self.original_price = 100
        # 折扣
        self.discount = 0.8

    @property
    def price(self):
        # 实际价格 = 原价 * 折扣
        new_price = self.original_price * self.discount
        return new_price

    @price.setter
    def price(self, value):
        self.original_price = value

    @price.deleter
    def price(self):
        del self.original_price

obj = Goods()
print(obj.price)  # 获取商品价格
obj.price = 200   # 修改商品原价
print(obj.price)  # 获取商品价格
del obj.price     # 删除商品原价
print(obj.price)  # 获取商品价格

执行结果:
80.0
160.0
AttributeError: 'Goods' object has no attribute 'original_price'
View Code

 

静态字段方式,创建值为property对象的静态字段。当使用静态字段的方式创建属性时,经典类和新式类无区别

property的构造方法中有个四个参数

  • 第一个参数是方法名,调用 对象.属性 时自动触发执行方法
  • 第二个参数是方法名,调用 对象.属性 = XXX 时自动触发执行方法
  • 第三个参数是方法名,调用 del 对象.属性 时自动触发执行方法
  • 第四个参数是字符串,调用 对象.属性.__doc__ ,此参数是该属性的描述信息
class Goods(object):

    def __init__(self):
        self.original_price = 100         # 原价
        self.discount = 0.8            # 折扣

    def get_price(self):   
        new_price = self.original_price * self.discount  # 实际价格 = 原价 * 折扣
        return new_price

    def set_price(self, value):
        self.original_price = value

    def del_price(self):
        del self.original_price

    #PRICE = property(get_price, set_price, del_price, '价格属性描述...')
    PRICE = property(get_price, set_price, del_price, '123')

obj = Goods()
print(obj.PRICE)         # 获取商品价格
obj.PRICE = 200   # 修改商品原价
print(obj.PRICE)         # 获取商品价格
print(obj.PRICE.__doc__)
del obj.PRICE     # 删除商品原价
print(obj.PRICE)         # 获取商品价格

不知道这里为什么获取不到doc
View Code

Python WEB框架 Django 的视图中 request.POST 就是使用的静态字段的方式创建的属性

 

posted @ 2017-08-12 17:31  bobo0609  Views(178)  Comments(0Edit  收藏  举报