类的基础知识
属性:类变量、实例变量、
方法:初始化方法 __init__ //初始化方法不是必须的;其他方法;
//类的定义
class Employee:
empCount = 0 //类变量,有些类似于静态变量,将在这个类的所有实例中共享
def __init__(self, name, salary):
//self代表类的实例,self在定义类的方法时必须有的,类的方法和普通函数的区别就在于必须要有额外的第一个参数self,
self.name = name
self.salary = salary
Employee.empCount +=1
def displayCount(self):
print "Total Employee %d" % Employee.empCount
def displayEmployee(self):
print "Name : ", self.name, ", Salary",self.salary
//类的实例化类似于函数调用的方式
emp1 = Employee("Zara",2000)
//可以用点号.来访问对象的属性
emp1.displayEmployee()
//甚至可以添加、删除、修改类的属性
emp1.age = 7 //添加age属性
emp2.age = 8 //修改age属性
del emp1.age //删除age属性
//python对象的销毁
Python 使用了引用计数这一简单技术来跟踪和回收垃圾。
//类的继承
面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。
通过继承创建的新类称为子类或派生类,被继承的类称为基类、父类或超类。
class 派生类名(基类名)
...
=====================================================
类的属性
类的属性广义上应该是包括变量和方法的。
即类的数据属性、方法属性。
通常情况下属性单指数据属性,即变量。
这一节讨论的属性属于数据属性
//内置类属性
__dict__ : 类的属性(包含一个字典,由类的数据属性组成)
__doc__ :类的文档字符串
__name__: 类名
__module__: 类定义所在的模块(类的全名是'__main__.className',如果类位于一个导入模块mymod中,那么className.__module__ 等于 mymod)
__bases__ : 类的所有父类构成元素(包含了一个由所有父类组成的元组)
//类的私有属性
__private_attrs:两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问。在类内部的方法中使用时 self.__private_attrs。
实例不能使用私有属性。只能使用self
//类的属性带单下划线和双下划线
单下划线代表属性私有,但是在内部和外部都可以调用,相当于一种约定。
双下划线一般情况下只能内部调用,外部不能调用,子类也不能调用。
但通过特殊的语法可以调用,原则上不用;
=====================================================
类变量和实例变量
通常来说,实例变量是对于每个实例都独有的数据,而类变量是该类所有实例共享的属性和方法。
其实我更愿意用类属性和实例属性来称呼它们,但是变量这个词已经成为程序语言的习惯称谓。一个正常的示例是:
class Dog:
kind = 'canine' # class variable shared by all instances
def __init__(self, name):
self.name = name # instance variable unique to each instance
类Dog
中,类属性kind
为所有实例所共享;实例属性name
为每个Dog
的实例独有。
=====================================================
类的方法
类的所有方法需要带上self参数;
没有构造函数,初始方法只是在实例化对象时会执行的方法;
self不是关键词,可以用其他单词代替,其代表类实例对象本身,不是类本身;
例如:类MyClass,类的实例myobject调用一个方法myobject.Method(arg1),
python会自动转为MyClass.Method(myobject, arg1)
//类的方法
在类的内部,使用 def 关键字可以为类定义一个方法,与一般函数定义不同,类方法必须包含参数 self,且为第一个参数
//类的私有方法
__private_method:两个下划线开头,声明该方法为私有方法,不能在类的外部调用。在类的内部调用 self.__private_methods
//类的方法的单下划线、双下划线、头尾双下划线说明:
__foo__: 定义的是特殊方法,一般是系统定义名字 ,类似 __init__() 之类的。
_foo: 以单下划线开头的表示的是 protected 类型的变量,即保护类型只能允许其本身与子类进行访问,不能用于 from module import *
__foo: 双下划线的表示的是私有类型(private)的变量, 只能是允许这个类本身进行访问了。
=====================================================
类对象和实例对象
//这里讲的属性属于广义的属性
类对象
Python
中一切皆对象;类定义完成后,会在当前作用域中定义一个以类名为名字,指向类对象的名字。如
class Dog:
pass
会在当前作用域定义名字Dog
,指向类对象Dog
。
类对象支持的操作:
总的来说,类对象仅支持两个操作:
- 实例化;使用
instance_name = class_name()
的方式实例化,实例化操作创建该类的实例。 - 属性引用;使用
class_name.attr_name
的方式引用类属性。
实例对象
实例对象是类对象实例化的产物,实例对象仅支持一个操作:
- 属性引用;与类对象属性引用的方式相同,使用
instance_name.attr_name
的方式。
按照严格的面向对象思想,所有属性都应该是实例的,类属性不应该存在。那么在Python
中,由于类属性绑定就不应该存在,类定义中就只剩下函数定义了。
在Python tutorial关于类定义也这么说:
In practice, the statements inside a class definition will usually be function definitions, but other statements are allowed, and sometimes useful.
实践中,类定义中的语句通常是函数定义,但是其他语句也是允许的,有时也是有用的。
这里说的其他语句,就是指类属性的绑定语句。
=====================================================
属性绑定
//绑定:有些类似于赋值
在定义类时,通常我们说的定义属性,其实是分为两个方面的:
- 类属性绑定
- 实例属性绑定
用绑定这个词更加确切;不管是类对象还是实例对象,属性都是依托对象而存在的。
我们说的属性绑定,首先需要一个可变对象,才能执行绑定操作,使用
objname.attr = attr_value
的方式,为对象objname
绑定属性attr
。
这分两种情况:
- 若属性
attr
已经存在,绑定操作会将属性名指向新的对象; - 若不存在,则为该对象添加新的属性,后面就可以引用新增属性。
类属性绑定
Python
作为动态语言,类对象和实例对象都可以在运行时绑定任意属性。因此,类属性的绑定发生在两个地方:
- 类定义时;
- 运行时任意阶段。
下面这个例子说明了类属性绑定发生的时期:
class Dog:
kind = 'canine'
Dog.country = 'China'
print(Dog.kind, ' - ', Dog.country) # output: canine - China
del Dog.kind
print(Dog.kind, ' - ', Dog.country) # AttributeError: type object 'Dog' has no attribute 'kind'
在类定义中,类属性的绑定并没有使用objname.attr = attr_value
的方式,这是一个特例,其实是等同于后面使用类名绑定属性的方式。
因为是动态语言,所以可以在运行时增加属性,删除属性。
实例属性绑定
与类属性绑定相同,实例属性绑定也发生在两个地方:
- 类定义时;
- 运行时任意阶段。
示例:
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
dog = Dog('Lily', 3)
dog.fur_color = 'red'
print('%s is %s years old, it has %s fur' % (dog.name, dog.age, dog.fur_color))
# Output: Lily is 3 years old, it has red fur
Python
类实例有两个特殊之处:
__init__
在实例化时执行Python
实例调用方法时,会将实例对象作为第一个参数传递
因此,__init__
方法中的self
就是实例对象本身,这里是dog
,语句
self.name = name
self.age = age
以及后面的语句
dog.fur_color = 'red'
为实例dog
增加三个属性name
, age
, fur_color
。
=====================================================
属性引用
属性的引用与直接访问名字不同,不涉及到作用域。
类属性引用
类属性的引用,肯定是需要类对象的,属性分为两种:
- 数据属性
- 函数属性
数据属性引用很简单,示例:
class Dog:
kind = 'canine'
Dog.country = 'China'
print(Dog.kind, ' - ', Dog.country) # output: canine - China
通常很少有引用类函数属性的需求,示例:
class Dog:
kind = 'canine'
def tell_kind():
print(Dog.kind)
Dog.tell_kind() # Output: canine
函数tell_kind
在引用kind
需要使用Dog.kind
而不是直接使用kind
,涉及到作用域,这一点在另一篇文章中有介绍:Python进阶 - 命名空间与作用域
实例属性引用
使用实例对象引用属性稍微复杂一些,因为实例对象可引用类属性以及实例属性。但是实例对象引用属性时遵循以下规则:
- 总是先到实例对象中查找属性,再到类属性中查找属性;
- 属性绑定语句总是为实例对象创建新属性,属性存在时,更新属性指向的对象。
数据属性引用
示例1:
class Dog:
kind = 'canine'
country = 'China'
def __init__(self, name, age, country):
self.name = name
self.age = age
self.country = country
dog = Dog('Lily', 3, 'Britain')
print(dog.name, dog.age, dog.kind, dog.country)
# output: Lily 3 canine Britain
类对象Dog
与实例对象dog
均有属性country
,按照规则,dog.country
会引用到实例对象的属性;但实例对象dog
没有属性kind
,按照规则会引用类对象的属性。
示例2:
class Dog:
kind = 'canine'
country = 'China'
def __init__(self, name, age, country):
self.name = name
self.age = age
self.country = country
dog = Dog('Lily', 3, 'Britain')
print(dog.name, dog.age, dog.kind, dog.country) # Lily 3 canine Britain
print(dog.__dict__) # {'name': 'Lily', 'age': 3, 'country': 'Britain'}
dog.kind = 'feline'
print(dog.name, dog.age, dog.kind, dog.country) # Lily 3 feline Britain
print(dog.__dict__)
print(Dog.kind) # canine 没有改变类属性的指向
# {'name': 'Lily', 'age': 3, 'country': 'Britain', 'kind': 'feline'}
使用属性绑定语句dog.kind = 'feline'
,按照规则,为实例对象dog
增加了属性kind
,后面使用dog.kind
引用到实例对象的属性。
这里不要以为会改变类属性Dog.kind
的指向,实则是为实例对象新增属性,可以使用查看__dict__
的方式证明这一点。
示例3,可变类属性引用:
class Dog:
tricks = []
def __init__(self, name):
self.name = name
def add_trick(self, trick):
self.tricks.append(trick)
d = Dog('Fido')
e = Dog('Buddy')
d.add_trick('roll over')
e.add_trick('play dead')
print(d.tricks) # ['roll over', 'play dead']
语句self.tricks.append(trick)
并不是属性绑定语句,因此还是在类属性上修改可变对象。
4.2.2 方法属性引用
与数据成员不同,类函数属性在实例对象中会变成方法属性。
先看一个示例:
class MethodTest:
def inner_test(self):
print('in class')
def outer_test():
print('out of class')
mt = MethodTest()
mt.outer_test = outer_test
print(type(MethodTest.inner_test)) # <class 'function'>
print(type(mt.inner_test)) #<class 'method'>
print(type(mt.outer_test)) #<class 'function'>
可以看到,类函数属性在实例对象中变成了方法属性,但是并不是实例对象中所有的函数都是方法。
Python tutorial中这样介绍方法对象:
When an instance attribute is referenced that isn’t a data attribute, its class is searched. If the name denotes a valid class attribute that is a function object, a method object is created by packing (pointers to) the instance object and the function object just found together in an abstract object: this is the method object. When the method object is called with an argument list, a new argument list is constructed from the instance object and the argument list, and the function object is called with this new argument list.
引用非数据属性的实例属性时,会搜索它对应的类。如果名字是一个有效的函数对象,Python会将实例对象连同函数对象打包到一个抽象的对象中并且依据这个对象创建方法对象:这就是被调用的方法对象。当使用参数列表调用方法对象时,会使用实例对象以及原有参数列表构建新的参数列表,并且使用新的参数列表调用函数对象。
那么,实例对象只有在引用方法属性时,才会将自身作为第一个参数传递;调用实例对象的普通函数,则不会。
所以可以使用如下方式直接调用方法与函数:
mt.inner_test()
mt.outer_test()
除了方法与函数的区别,其引用与数据属性都是一样的
=====================================================
参考链接:
类变量和实例变量:https://www.cnblogs.com/crazyrunning/p/6945183.html