首先,一句话:Python连类本身都是对象。。。对类本身可以各种玩弄。。。
一、创建类的属性
类本身也是一个对象,如果在类上绑定一个属性,则所有实例都可以访问类的属性,并且,所有实例访问的类属性都是同一个!也就是说,实例属性每个实例各自拥有,互相独立,而类属性有且只有一份。一个实例修改了类成员,另一个实例也看到了修改。当然,废话。
类的属性的读取可以用实例、也可以用类本身
class Leechanx:
address = 'Earth'
p1 = Leechanx()
p2 = Leechanx()
p1.address = 'China'
print p2.address 结果却仍然是Earth
print Person.address 结果却仍然是Earth
我们发现,在设置了 p1.address = 'China' 后,p1访问 address 确实变成了 'China',但是,Person.address和p2.address仍然是'Earch',怎么回事?
原因是 p1.address = 'China'并没有改变 Person 的 address,而是给 p1这个实例绑定了实例属性address ,对p1来说,它有一个实例属性address(值是'China'),而它所属的类Person也有一个类属性address,所以:
访问 p1.address 时,优先查找实例属性,返回'China'。
访问 p2.address 时,p2没有实例属性address,但是有类属性address,因此返回'Earth'。
可见,当实例属性和类属性重名时,实例属性优先级高,它将屏蔽掉对类属性的访问
二、访问限制
Python对属性权限的控制是通过属性名来实现的,如果一个属性由双下划线开头(__,不过同时不以双下划线结尾),该属性就无法被外部访问(无论类属性、实例属性)。原理是Python会隐式地将类中__双下划线开头的成员变量改名为"_类名__xx"。
class Person(object):
__id = 0
def __init__(self, name):
self.__job = 'Student’
#这里用一个实例属性__job,一个类属性__id为例子
p = Person('Bob')
print p.__job # => Error
print Person.__id #=>Error
print dir(p)后,发现__job\__id变量被类修改为_Person__job\_Person__id了,就是靠改名字不让别人访问的。。。好搓。。。
可见,以双下划线开头的”__job”\”__id"不能直接被外部访问。
所以,以双下划线开头的变量(同时不以双下划线结尾)的成员变量是类的私有变量。
PS:Python的下划线完全解读
单下划线_xxx 不能用’from module import *’导入,常用于模块中,在一个模块中以单下划线开头的变量和函数被默认当作内部函数,如果使用 from a_module import * 导入时,这部分变量和函数不会被导入。不过值得注意的是,如果使用 import a_module 这样导入模块,仍然可以用a_module._xxx 这样的形式访问到这样的对象;
双下划线 __xxx 类中的私有变量名;
双下划线 __xxx__ 系统定义名字,是系统已经存在的成员,比如__init__、__new__、__len__等
三、获取类的信息(Python自省)
isinstance(s, Student) //判断s是不是Student的实例
type(s) //找出s属于哪个类
dir(s), dir(Student) //获取实例对象、类的属性们
getattr(object, name) //获取对象的某属性的值
setattr(object, name, value) //设置对象的某属性的值
hasattr(object, name) //判断对象是否有某属性
四、类的特殊方法
对类的某些操作会自动调用的特殊方法,是系统方法。不需要显式调用,会由Python隐式调用之。
print => __str__
交互式>>obj => __repr__
cmp操作 => __cmp__
len获取长度 => __len__
让类的实例变成可调用的变量 => __call__
限定类的属性 => __slots__
@property 为类的属性设置getter与setter
1、让类的实例变成可调用的变量 => __call__
Python中,函数本身也是对象,又由于函数可调用,所以是可调用对象;类的实例也可以变成可调用对象,只要类实现了__call__,实例就可以像函数一样被调用
p = SomeClass()
p(若干参数)
一个例子:
class Person(object):
def __init__(self, name):
self.name = name
def __call__(self, friend):
print 'My name is %s...' % self.name
print 'My friend is %s...' % friend
p = Person('Bob')
p('Tim')
2、限制类和类实例可添加的属性 => __slots__
由于Python是动态语言,一个类的实例可以在运行时任何时候添加新的属性
如果要限制添加的属性,例如,Student类只允许添加 name、gender和score 这3个属性,就可以利用Python的一个特殊的__slots__来实现。
顾名思义,__slots__是指一个类允许的属性列表:
class Student(object):
__slots__ = ('name', 'gender', 'score')
def __init__(self, name, gender, score):
self.name = name
self.gender = gender
self.score = score
__slots__的目的是限制当前类所能拥有的属性
3、为属性添加简易的Getter和Setter => @property
考察 Student 类:
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
当我们想要修改一个Student 的 scroe 属性时,可以这么写:
s = Student('Bob', 59)
s.score = 60
但是也可以这么写:
s.score = 1000
显然,直接给属性赋值无法检查分数的有效性。
如果利用两个方法:
class Student(object):
def __init__(self, name, score):
self.name = name
self.__score = score
def get_score(self):
return self.__score
def set_score(self, score):
if score 100:
raise ValueError('invalid score')
self.__score = score
这样一来,s.set_score(1000) 就会报错。
这种使用 get/set 方法来封装对一个属性的访问在许多面向对象编程的语言中都很常见。
但是写 s.get_score() 和 s.set_score() 没有直接写 s.score 来得直接。
有没有两全其美的方法?----有。
因为Python支持高阶函数,在函数式编程中我们介绍了装饰器函数,可以用装饰器函数把 get/set 方法“装饰”成属性调用:
class Student(object):
def __init__(self, name, score):
self.name = name
self.__score = score
@property
def score(self):
return self.__score
@score.setter
def score(self, score):
if score 100:
raise ValueError('invalid score')
self.__score = score
第一个score(self)是get方法,用@property装饰,第二个score(self, score)是set方法,用@score.setter装饰,@score.setter是前一个@property装饰后的副产品。
现在,就可以像使用属性一样设置score了:
>>> s = Student('Bob', 59)
>>> s.score = 60 #将调用__score的set方法:@score.setter:def score(self, score)...
>>> print s.score #将调用__score的get方法:@property:def score(self)
五、静态方法和类方法
一般的实例方法需要绑定self,传参self;
类方法需要绑定类,传参cls;
静态方法不需要绑定任何东西;
一个简单的例子:
class Solution:#静态方法的例子
def foo():
pass
foo = staticmethod(foo) #这一行是必须的,指明foo是静态方法
class Solution:#类方法的例子
def foo(cls):
pass
foo = classmethod(foo)
可以调用的方式包括:
Solution.foo()
Solution.bar()
ins.foo()
ins.bar()
由于foo = staticmethod(foo)、foo = classmethod(foo)看起来太逗比,用修饰符@staticmethod、@classmethod来代替:
class Solution:#静态方法的例子
@staticmethod
def foo():
pass
class Solution:#类方法的例子
@classmethod
def foo(cls):
pass
目前觉得吧,这个staticmethod、classmethod两者没什么卵区别。。。
六、super关键字的作用
比如子类重写了父类的方法,如果子类的方法中想调用父类的这个被重写方法怎么办?用super;
super(Child, self).func() super找到Child的基类,并将self传给基类的func()函数,这样就可以调用基类被重写的func了。
七、Python多态
动态语言对多态拥有良好的支持。
class Animal:
def whoAmI(self):
print ‘I am Animal'
class Cat(Animal):
def whoAmI(self):
print ‘I am Cat'
class Dog(Animal):
def whoAmI(self):
print ‘I am Dog'
def whoAreU(x):x.whoAmI()
i = Animal()
j = Cat()
k = Dog()
whoAreU(i) #输出‘I am Animal'
whoAreU(j) #输出‘I am Cat'
whoAreU(k) #输出’I am Dog'
有继承,才有多态;多态的好处就是,当我们需要传入Dog、Cat、Tortoise……时,我们只需要接收Animal类型就可以了,因为Dog、Cat、Tortoise……都是Animal类型,然后,按照Animal类型进行操作即可。由于Animal类型有run()方法,因此,传入的任意类型,只要是Animal类或者子类,就会自动调用实际类型的whoAmI()方法,这就是多态的意思:
对于一个变量,我们只需要知道它是Animal类型,无需确切地知道它的子类型,就可以放心地调用whoAmI()方法,而具体调用的whoAmI()方法是作用在Animal、Dog、Cat还是Tortoise对象上,由运行时该对象的确切类型决定,这就是多态真正的威力:调用方只管调用,不管细节,而当我们新增一种Animal的子类时,只要确保whoAmI()方法编写正确,不用管原来的代码是如何调用的。
八、__init__和__new__的说道
一个类的实例创建,首先调用类的静态方法:__new__创建对象,并返回对象;然后调用第一个实例方法__init__,将对象进行初始化,__init__返回None。
__new__是一个静态方法,而__init__是一个实例方法;__new__方法会返回一个创建的实例,而__init__什么都不返回.
只有在__new__返回一个cls的实例时后面的__init__才能被调用;
当创建一个新实例时调用__new__,初始化一个实例时用__init__