在面向对象基础篇中,我们讲述了面向对象的很多基础知识,但也有很多限于篇幅并没有涉及到,这里通过进阶篇来完善补充。本篇将详细介绍Python 类的成员、成员修饰符。

一. python类的成员

以下内容转自http://www.cnblogs.com/wupeiqi/p/4766801.html
类的成员可以分为三大类:变量(属性)、方法和属性方法。

有的地方也称变量为字段,成员变量就是普通字段,类变量就是静态字段。

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

1.1 变量

成员变量属于对象(对象实例化的时候就创建出来,一对一的关系),为每个对象私有,不共用。类变量属于类,在类中直接定义(并非在构造函数中定义),也称为类的公有属性,只要类被访问,就已经生成,为类为模板所产生的对象object所共有,是提供给这个类所属的所有对象都可以访问的属性全局仅一份拷贝。成员变量和类变量的定义和用法如下:

  1 class Province(object):
2
3 # 静态字段
4 country = '中国'
5
6 def __init__(self, name):
7
8 # 普通字段
9 self.name = name
10
11
12 # 直接访问普通字段
13 obj = Province('河北省')
14 print(obj.name)
15
16 # 直接访问静态字段
17 print(Province.country)
18 # 通过对象来访问类变量
19 print(obj.country)
20
21 结果输出:
22 河北省
23 中国
24 中国

由上述代码可以看出【普通字段需要通过对象来访问】【静态字段通过类访问,也可以通过类的实例化对象来访问,因为属于类中可共享访问的属性】,在使用上可以看出普通字段和静态字段的归属是不同的。其在内容的存储方式类似如下图:

由上图可知:

  • 类变量在内存中只保存一份,所以称之为类的公有属性
  • 成员变量与实例化的对象一一绑定,每个对象都有自己专属私有的成员变量(不同的对象,在实例化时传递的成员变量值可能相同,也可能不同,但对于对象来说是唯一的)

应用场景: 通过类创建对象时,如果每个对象都具有相同的字段,那么就使用类变量(公有属性)

  • 类变量公有属性的修改

类变量作为一种直接在类中定义的公有属性,定义后能否修改呢?答案是肯定的,而且看起来可以通过类和对象两个维度去修改,实际过程如下:

  1 class dog(object):
2 "dog class"
3
4 nationality = "JP"
5
6 def __init__(self,name):
7 self.name = name
8
9 d1 = dog("alex")
10 d2 = dog("sanjiang")
11 print(d1.nationality,d2.nationality)
12 print("before change ...")
13 d1.nationality = "CN" #对象的d1修改公共属性得值
14 print(d1.nationality,d2.nationality)
15 print("after change ....")
16 dog.nationality = "US" #dog类修改公共属性的值
17 print(d1.nationality,d2.nationality)
18
19 #输出
20 JP JP
21 brfore change ...
22 CN JP #d1对象的公共属性被修改了
23 after change ....
24 CN US #d1对象的公共属性值没有随着类本身的公共属性值修改而修改

以上代码看起来同时通过类和对象对类的公有属性进行了修改, 但实际上类变量只能通过类来进行修改, 通过对象来修改只是表象, 本质上是对象内部又重新创建了一个局部变量, 它与类变量重名而已, 实际与类变量没有任何关系.
上图把(以下图片和文字转自https://www.cnblogs.com/zhangqigao/articles/6885530.html):

对上面的图做一下总结:

  1. 对象d1去访问nationality属性时,如果在成员属性中找不到,就会找公共属性,也就是说自己的属性找不到就去找父亲的属性

  2. d1.nationality="CN" 相当于在自己对象内部又重新创建了一个新的局部变量,这个局部变量已经脱离了class本身,跟这个类已经没有半毛钱关系了,只是名字一样而已,如果不改,还是找全局的。

1.2 方法

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

  • 普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self
  • 类方法:可以通过类和实例调用; 至少一个cls参数;执行类方法时,自动将调用该方法的复制给cls,即cls代表类本身(类似于self代表实例化对象一样);也可以根据需要自定义其他参数,但至少得有一个参数,默认会自动设置为cls
  • 静态方法:可以通过类和实例调用;无默认参数;
  1. 普通方法
    除静态方法与类方法外,类的其他方法都属于实例方法。实例方法需要将类实例化后调用,如果使用类直接调用实例方法,需要显式地将实例作为参数传入。最左侧传入的参数self,是实例本身。

      1 # !/usr/bin/env python
    2 # -*- coding: utf-8 -*-
    3 __author__ = 'Maxwell'
    4
    5 class Dog(object):
    6 name = "AGou"
    7
    8 def __init__(self, name):
    9 self.name = name
    10
    11
    12 def eat(self, food):
    13 print('%s is eatting %s' % (self.name, food))
    14
    15 dog1 = Dog('XiaoHuang')
    16 dog1.eat('hotdog')
    17
    18 输出:
    19 XiaoHuang is eatting hotdog
  2. 类方法
    类方法使用 @classmethod 装饰器,可以使用类(也可使用实例)来调用方法。类只能访问类变量(又叫静态属性),不能访问实例变量。 实际可用于访问限定死的变量,比如朝鲜不允许修改国籍。以下示例一下类只能访问类变量,不能访问示例变量的情况:
    访问实例变量直接报错

      1 # !/usr/bin/env python
    2 # -*- coding: utf-8 -*-
    3 __author__ = 'Maxwell'
    4
    5 class Dog(object):
    6 #name = "AGou"
    7
    8 def __init__(self, name):
    9 self.name = name
    10
    11 @classmethod
    12 def eat(self, food):
    13 print('%s is eatting %s' % (self.name, food))
    14
    15 dog1 = Dog('XiaoHuang')
    16 dog1.eat('hotdog')
    17
    18 输出:
    19 Traceback (most recent call last):
    20 File "D:/python/S13/Day6/c3.py", line 16, in <module>
    21 dog1.eat('hotdog')
    22 File "D:/python/S13/Day6/c3.py", line 13, in eat
    23 print('%s is eatting %s' % (self.name, food))
    24 AttributeError: type object 'Dog' has no attribute 'name'

    访问类变量:

      1 # !/usr/bin/env python
    2 # -*- coding: utf-8 -*-
    3 __author__ = 'Maxwell'
    4
    5 class Dog(object):
    6 name = "AGou"
    7
    8 def __init__(self, name):
    9 self.name = name
    10
    11 @classmethod
    12 def eat(self, food):
    13 print('%s is eatting %s' % (self.name, food))
    14
    15 dog1 = Dog('XiaoHuang')
    16 dog1.eat('hotdog')
    17
    18 输出:
    19 AGou is eatting hotdog

    前面说过类方法至少得有一个cls参数,可以通过类方法的cls来访问类的属性或方法:

      1 # !/usr/bin/env python
    2 # -*- coding: utf-8 -*-
    3 __author__ = 'Maxwell'
    4
    5 class Dog(object):
    6 name = "AGou"
    7
    8 def __init__(self, name):
    9 self.name = name
    10
    11 def walk(self):
    12 print('%s is walking' % self.name )
    13
    14 @classmethod
    15 def class_method(cls, food):
    16 print('%s is eatting %s' % (cls.name, food))
    17 cls.walk(cls)
    18
    19 dog1 = Dog('XiaoHuang')
    20 dog1.class_method('hotdog')
    21
    22 输出:
    23 AGou is eatting hotdog
    24 AGou is walking
  3. 静态方法
    在类中往往有一些方法跟类有关系,但是又不会改变类和实例状态的方法,这种方法是静态方法。静态方法只是名义上归类管理,实际上在静态方法里访问不了类或实例中的任何属性。静态方法没有默认参数要求,本质上它与类没有什么直接关系。可以通过类或者实例化对象来调用静态方法,如果静态方法的参数中定义了self,无论是通过类还是通过对象来调用,self这个位置参数都不能省略,必须显式传入它自己进去(对象名或者类名)。因此有这样一句话:静态方法与类的唯一关系是,调用它必须通过类或者实例来调用,如果有定义self参数,那么调用的时候self参数不能省略,必须显式传进去

      1 # -*- coding: utf-8 -*-
    2 __author__ = 'Maxwell'
    3
    4 class Dog(object):
    5 name = "AGou"
    6
    7 def __init__(self, name):
    8 self.name = name
    9
    10
    11 @staticmethod
    12 def eat(self, food):
    13 print('%s is eatting %s' % (self.name, food))
    14
    15 dog1 = Dog('XiaoHuang')
    16 dog1.eat(dog1, 'hotdog')
    17 Dog.eat(Dog, 'hotdog')
    18
    19 输出:
    20 XiaoHuang is eatting hotdog
    21 AGou is eatting hotdog



    这里如果在调用时不显式传入self参数,运行时会报错TypeError: eat() missing 1 required positional argument: 'food'
    如果静态方法没有定义self参数,也可以正常调用,我们只需要像调用普通函数一样确保参数ok即可:

      1 # !/usr/bin/env python
    2 # -*- coding: utf-8 -*-
    3 __author__ = 'Maxwell'
    4
    5 class Dog(object):
    6 name = "AGou"
    7
    8 def __init__(self, name):
    9 self.name = name
    10
    11
    12 @staticmethod
    13 def eat(food):
    14 print('eating %s' % (food))
    15
    16 dog1 = Dog('XiaoHuang')
    17 dog1.eat('hotdog')
    18 print('-----')
    19 Dog.eat('hotdog')
    20
    21 输出:
    22 eating hotdog
    23 -----
    24 eating hotdog

    可以看到,静态方法没有默认的self和cls参数,可以把它看成是一个普通的函数,我们当然可以把它写到类外面,但这是不推荐的,因为这不利于代码的组织和命名空间的整洁。

1.3 属性方法

以下内容还是继续转载,源自http://www.cnblogs.com/wupeiqi/p/4766801.html

  • 属性方法简介

如果你已经了解Python类中的方法,那么属性就非常简单了,因为Python中的属性其实是普通方法的变种。在方法名前加上@property装饰器,表示此方法为属性方法。属性方法的基本使用如下:

  1 # ############### 定义 ###############
2 class Foo:
3
4 def func(self):
5 pass
6
7 # 定义属性
8 @property
9 def prop(self):
10 pass
11 # ############### 调用 ###############
12 foo_obj = Foo()
13
14 foo_obj.func()
15 foo_obj.prop #调用属性

由属性的定义和调用要注意一下几点:

(1)定义时,在普通方法的基础上添加 @property 装饰器;

(2)定义时,属性仅有一个self参数

(3)调用时,无需括号
        方法:foo_obj.func()
        属性:foo_obj.prop

注意:属性存在意义是:访问属性时可以制造出和访问字段完全相同的假象,因为实际上是把一个方法变成可调用的属性,如果不采用特殊方式处理,它还是一个只读属性

(下文会详述)

属性由方法变种而来,如果Python中没有属性,方法完全可以代替其功能

  • 属性方法的应用场景

通过上面的介绍可以看出属性方法本质上是一个方法,但返回一个属性值,方法内部可以封装我们的处理逻辑,但对外仅仅返回一个简单的结果即可,这也是属性方法的功能所在。因此可以用于在类中封装较为复杂的处理逻辑。

例子1:
      以下内容部分转自https://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001386820062641f3bcc60a4b164f8d91df476445697b9e000

设想一个场景,我们对类的对象绑定一个属性时,实现起来很简单,但不能对属性值进行合法性检查,导致属性值可以任意修改:

  1 __author__ = 'Maxwell'
2
3 class Student():
4 def set_score(self, score):
5 self.score = score
6
7 def get_score(self):
8 return self.score
9
10 stdu1 = Student()
11 stdu1.set_score(9999)
12 stdu1.get_score()

这显然有违逻辑,如果要对分数的合法性进行检查要怎么办呢?

  1 class Student():
2 def set_score(self, score):
3 if isinstance(int, score) and int(score) > 0 and int(score) <= 100:
4 self.score = score
5 else:
6 raise ValueError('score must be an integer!')
7
8 def get_score(self):
9 return self.score
10
11 stdu1 = Student()
12 stdu1.set_score(9999)
13 stdu1.get_score()

为了限制score的范围,可以通过一个set_score()方法来设置成绩,再通过一个get_score()来获取成绩,这样,在set_score()方法里,就可以检查参数。但是这里的过程略显复杂,没有像调用属性一样那么简单。

有没有既能检查参数,又可以用类似属性这样简单的方式来访问类的变量呢?属性方法就可以实现:

  1 class Student():
2 @property
3 def score(self):
4 return self.__score
5
6 @score.setter
7 def score(self, score):
8 if isinstance(score, int) and int(score) > 0 and int(score) <= 100:
9 self.__score = score
10 else:
11 raise ValueError('score must be an integer!')
12
13 stdu1 = Student()
14 stdu1.score = 60
15 print(stdu1.score)
16
17 输出:
18 60

从这里可以看出属性方法还有一个神技就是能把类的私有变量变成可通过对象直接访问。

我们在对实例属性操作的时候,就知道该属性很可能不是直接暴露的,而是通过getter和setter方法来实现的。还可以定义只读属性,只定义getter方法,不定义setter方法就是一个只读属性:

  1 class Student():
2 @property
3 def birth_year(self):
4 return self.__birth_year
5
6
7 @birth_year.setter
8 def birth_year(self, birth_year):
9 self.__birth_year = birth_year
10
11
12 @property
13 def age(self):
14 return 2018 - self.__birth_year
15
16 Stud1 = Student()
17 Stud1.birth_year = 1990
18 print(Stud1.age)
19
20 输出:
21 28

上述程序中的__birth_year通过setter设置值,但age是通过__birth_year计算出来的,不可认为设置,它是一个只读属性。

例子2:
以下例子还是转自大师级别的博客http://www.cnblogs.com/wupeiqi/p/4766801.html

  1 __author__ = 'Maxwell'
2
3 class Goods(object):
4
5 def __init__(self):
6 # 原价
7 self.original_price = 100
8 # 折扣
9 self.discount = 0.8
10
11 @property
12 def price(self):
13 # 实际价格 = 原价 * 折扣
14 new_price = self.original_price * self.discount
15 return new_price
16
17 @price.setter
18 def price(self, value):
19 self.original_price = value
20
21 @price.deleter
22 def price(self):
23 del self.original_price
24
25 obj = Goods()
26 print(obj.price) # 获取商品价格
27 obj.price = 200 # 修改商品原价
28 print(obj.price)
29 del obj.price # 删除商品原价
30 print(obj.price)
31
32 输出:
33 Traceback (most recent call last):
34 File "D:/python/S13/Day6/testclass.py", line 32, in <module>
35 print(obj.price)
36 File "D:/python/S13/Day6/testclass.py", line 16, in price
37 new_price = self.original_price * self.discount
38 AttributeError: 'Goods' object has no attribute 'original_price'
39 80.0
40 160.0

上述程序调用deleter之后,会发现报错提示'Goods' object has no attribute 'original_price', 表明'original_price'已经被成功清理。

  • 属性方法的getter、setter和deleter

getter、setter和deleter是property的具体方法上外加装饰器的用法,分别表示获取属性、设置属性和删除属性,实际上getter并没有显示应用,直接应用property装饰器就是应用了getter。对于deleter,需要注意的是:

(1)setter定义方法后,实际调用时是直接用赋值的方式来进行的,Obj_name.method = xyz

(2)deleter不仅要在属性方法中定义delete的属性,在调用时还要显式地写明del Obj_name.Property

由于上面两个例子已经展示了具体用法,这里不再赘述。

二、 python类的成员修饰符

类的成员修饰符,前面并没有深入讲述,这里补充一下。没办法,大师讲的好,还是转载一下吧http://www.cnblogs.com/wupeiqi/p/4766801.html

对于每一个类的成员而言都有两种形式:

  • 公有成员,在任何地方都能访问
  • 私有成员,只有在类的内部才能方法

私有成员和公有成员的定义不同:私有成员命名时,前两个字符是下划线。(特殊成员除外,例如:__init__、__call__、__dict__等)

  1 class C:
2
3 def __init__(self):
4 self.name = '公有字段'
5 self.__foo = "私有字段"

私有成员和公有成员的访问限制不同

静态字段

  • 公有静态字段:类可以访问;类内部(实例化对象)可以访问;派生类中可以访问
  • 私有静态字段:仅类内部(对象)可以访问

  1 class C:
2
3 name = "公有静态字段"
4
5 def func(self):
6 print(C.name)
7
8 class D(C):
9
10 def show(self):
11 print(C.name)
12
13
14 C.name # 类访问
15
16 obj = C()
17 obj.func() # 类内部可以访问
18
19 obj_son = D()
20 obj_son.show() # 派生类中可以访问

  1 class C:
2
3 __name = "公有静态字段"
4
5 def func(self):
6 print(C.__name)
7
8 class D(C):
9
10 def show(self):
11 print(C.__name)
12
13
14 print(C.__name) # 类访问 ==> 错误,会提示没有该属性
15
16 obj = C()
17 obj.func() # 类内部可以访问 ==> 正确
18
19 obj_son = D()
20 obj_son.show() # 派生类中访问 ==> 错误,会提示没有该属性

普通字段

  • 公有普通字段:对象可以访问;类内部可以访问;派生类中可以访问
  • 私有普通字段:仅类内部可以访问;

ps:如果想要强制访问私有字段,可以通过 【对象._类名__私有字段明 】访问(如:obj._C__foo),不建议强制访问私有成员。

  1 class C:
2
3 def __init__(self):
4 self.foo = "公有普通字段"
5
6 def func(self):
7 print(self.foo)  # 类内部访问
8
9 class D(C):
10
11 def show(self):
12 print(self.foo) # 派生类中访问
13
14 obj = C()
15
16 obj.foo # 通过对象访问
17 obj.func() # 类内部访问
18
19 obj_son = D();
20 obj_son.show() # 派生类中访问

  1 class C:
2
3 def __init__(self):
4 self.__foo = "私有普通字段"
5
6 def func(self):
7 print self.foo  # 类内部访问
8
9 class D(C):
10
11 def show(self):
12 print self.foo # 派生类中访问
13
14 obj = C()
15
16 obj.__foo # 通过对象访问 ==> 错误
17 obj.func() # 类内部访问 ==> 正确
18
19 obj_son = D();
20 obj_son.show() # 派生类中访问 ==> 错误

方法、属性的访问于上述方式相似,即:私有成员只能在类内部使用

ps:非要访问私有属性的话,可以通过 对象._类__属性名

day6-面向对象进阶篇的更多相关文章

  1. Python开发【第七篇】:面向对象 和 python面向对象进阶篇(下)

    Python开发[第七篇]:面向对象   详见:<Python之路[第五篇]:面向对象及相关> python 面向对象(进阶篇)   上一篇<Python 面向对象(初级篇)> ...

  2. Python 面向对象 (进阶篇)

    <Python 面向对象(初级篇)>文章介绍了面向对象基本知识: 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 是一个模板,模板中包装了多个“函数”供使用(可 ...

  3. Java面向对象进阶篇(包装类,不可变类)

    一. Java 8的包装类 Java中的8种基本数据类型不支持面向对象的变成机制,也不具备对象的特性:没有成员变量,方法可以调用.为此,Java为这8 种基本数据类型分别提供了对应的 包装类(Byte ...

  4. Java面向对象进阶篇(抽象类和接口)

    一.抽象类 在某些情况下,父类知道其子类应该包含哪些方法,但是无法确定这些子类如何实现这些方法.这种有方法签名但是没有具体实现细节的方法就是抽象方法.有抽象方法的类只能被定义成抽象类,抽象方法和抽象类 ...

  5. Java面向对象进阶篇(内部类)

    一. 概念 大部分时候,类被定义成一个独立的程序单元.有时候把一个类放在另一个类内部定义,这个类被称为内部类,包含内部类的类也被称为外部类. 内部类的主要作用: 内部类提供良好的封装,可以把内部类隐藏 ...

  6. python之路 面向对象进阶篇

    一.字段 字段包括:普通字段和静态字段,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同, 普通字段属于对象 静态字段属于类 class Province: # 静态字段 countr ...

  7. python 面向对象(进阶篇)

    上一篇<Python 面向对象(初级篇)>文章介绍了面向对象基本知识: 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 是一个模板,模板中包装了多个“函数”供使 ...

  8. 【转】python 面向对象(进阶篇)

    [转]python 面向对象(进阶篇) 上一篇<Python 面向对象(初级篇)>文章介绍了面向对象基本知识: 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 ...

  9. python 面向对象(进阶篇)转载武沛齐

    上一篇<Python 面向对象(初级篇)>文章介绍了面向对象基本知识: 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 是一个模板,模板中包装了多个“函数”供使 ...

  10. Python基础—面向对象(进阶篇)

    通过上一篇博客我们已经对面向对象有所了解,下面我们先回顾一下上篇文章介绍的内容: 上篇博客地址:http://www.cnblogs.com/phennry/p/5606718.html 面向对象是一 ...

随机推荐

  1. cdoj1638 红藕香残玉簟秋,轻解罗裳,独上兰舟。

    地址:http://acm.uestc.edu.cn/#/problem/show/1638 题目: 红藕香残玉簟秋,轻解罗裳,独上兰舟. Time Limit: 4000/2000MS (Java/ ...

  2. Java枚举类型定义方式

    public enum Method { // get请求 GET("get"), // post请求 POST("post"); private String ...

  3. 51nod 1391 01串(hash+DP)

    题目链接题意:给定一个01串S,求出它的一个尽可能长的子串S[i..j],满足存在一个位置i<=x <=j, S[i..x]中0比1多,而S[x + 1..j]中1比0多.求满足条件的最长 ...

  4. JSP 与 Servlet 的关系

    以下摘自维基百科: Java服务器页面(JSP)是HttpServlet的扩展.由于HttpServlet大多是用来响应HTTP请求,并返回Web页面(例如HTML.XML),所以不可避免地,在编写s ...

  5. centos7下配置iptables实现外网访问内网服务器

    说明:Centos 7 默认的防火墙是 firewall,安装iptables之前需关闭Firewall 外网机器:外网ip:120.25.71.183内网ip:10.1.1.23 内网机器:内网ip ...

  6. iOS 动态调用方法

      - (void)bugly { dispatch_async(dispatch_get_global_queue(0, 0), ^{ if (NSClassFromString(@"Bu ...

  7. springCloud3---ribbon

    同一份代码,改变端口,就可以启动多个同名但是端口不一样的微服务. 客户端通过nginx来调用后面的多个用户微服务来实现负载均衡,这是服务端负载均衡. 客户端有一个组件,可以知道当前有几个用户微服务的i ...

  8. 联想(Lenovo)小新310经典版进bios方法

    1,找到novo按钮. 2,在关机的状态下桶一下小孔,不用任何操作,电脑进入bios选择界面.

  9. Python3.x:代理ip刷点赞

    Python3.x:代理ip刷点赞 声明:仅供为学习材料,不允许用作商业用途: 一,功能: 针对某网站对企业自动刷点赞: 网站:https://best.zhaopin.com/ 二,步骤: 1,获取 ...

  10. 20145331 《Java程序设计》第2次实验报告

    20145331 <Java程序设计>第2次实验报告 实验二 Java面向对象程序设计 一.实验内容 1.初步掌握单元测试和TDD 2.理解并掌握面向对象三要素:封装.继承.多态 3.初步 ...