1. 类的创建
类是对某个对象的定义,它包含有关对象动作方式的信息,包括它的名称、方法、属性和事件。
类不存在于内存中,因此它本身并不是对象。当程序运行需要引用类的代码时,就会在内存中创建一个类的新实例,即对象。
虽然只有一个类,但能以这个类在内存中创建多个相同类型的对象。
class Person(object):
#类的方法中必须要有一个self参数,但是方法被调用时,不用传递这个参数
def get_name(self):
print "my name is: lili"
def get_age(self):
print "my age is : 20"
def get_hoppy(self):
print "My hoppy is :lvyou"
boy = Person()
boy.get_name() #但是方法被调用时,不用传递这个参数
boy.get_age() #在调用get_age等方法时,boy自动将自己作为一个参数传入方法中,因此我们称它为self
boy.get_hoppy()
使用 PyCharm
在类中定义方法时,若该方法不涉及对属性的操作,那么会提示 Method xxx may be 'static'
,因为 PyCharm
会认为该方法是一个静态方法,而不是类方法,所提提示我们在该方法前添加 @staticmethod
装饰器进行装饰。
2. 类的封装和多态
所谓封装,即将抽象得到的数据和行为(功能)相结合,形成一个有机整体,也就是将数据和操作数据的源代码进行有机整合,形成类。
其中数据和函数都是类的成员。目的是隐藏对象的属性和实现细节,对外公开接口,在程序中控制属性的读和修改的访问级别。
封装和多态的区别:多态可以让用户对不知道是什么类的对象进行方法调用,而封装则不用关心对象是如何创建的而直接进行使用。
class MyClass(object):
# 定义一个全局变量
name = 'xml'
def set_name(self, name):
self.name = name
def get_name(self):
return self.name
my = MyClass()
my.set_name('Jack')
print my.get_name()
# 如果将变量存储到全局变量 name 中时,实例化 MyClass 之后,全局变量 name 将会改变
my = MyClass()
my.name = 'Tom'
print my.get_name()
# 对象有自己的状态,对象的状态由它的特性名称来描述,eg:下面 she 对象的内容不影响 my 对象的内容
she = MyClass()
she.set_name('angel')
print she.get_name()
print my.get_name()
3. 类的属性
-
私有属性
函数、方法或者属性的名称以两个下划线
_
开始,则为私有类型;
以单下划线开头(_foo
)的代表不能直接访问的类属性,需通过类提供的接口进行访问,不能用 from xxx import
导入;
以双下划线开头的(__foo
)代表类的私有成员;
以双下划线开头和结尾的(__ foo__
)代表Python里特殊方法专用的标识,如__init()__
代表类的构造函数;
__attribute
:属性名前面加两个下划线,即声明该属性为私有,不能在类的外部直接访问,在类内部访问时用self.__attribute
。__function
:函数名前面加两个下划线,即声明该函数为私有,不能在类的外部直接访问,在类内部访问时用self.__function
。
-
公有属性
如果函数、方法或者属性的名称没有以两个下划线开始,则为公有属性;
-
实例属性
以
self
作为前缀的属性; -
局部变量
类的方法中定义的变量没有使用
self
作为前缀声明,则该变量为局部变量;
class Fly():
price = 123 # 类的公有属性
def __init__(self):
self.direction = "beijing" # 类的公有实例属性
self.__city = '陕西' # 私有的实例属性
isPeople = "more people" # 局部变量
if __name__ == '__main__':
print Fly.price
#print Fly.direction 公有的实例属性在未实例化之前不能使用,该语句是错误的
traveller = Fly()
print traveller.direction
#print traveller.__city Fly instance has no attribute '__city'
print traveller._Fly__city #私有属性的访问方式:实例化对象名._类名__私有属性名
#print traveller.isPeople 局部变量不能被实例化对象traveller所引用
Fly.price = traveller.price + 10
print "the Fly prise is " + str(Fly.price)
mytraveller = Fly()
print "I think the price is " + str(mytraveller.price)
数据属性不需要预先定义,数据属性初次被使用时,即被创建并赋值。
class Data_attribute():
pass
if __name__ == "__main__":
data = Data_attribute()
data.name = "我是没有被预先定义的数据"
print data.name
3.1 类数据属性
- 类数据属性属于类本身,可以通过类名进行访问、修改
- 类数据属性也可以被类的所有实例访问、修改
- 在类定义之后,可以通过类名动态添加类数据属性,新增的类属性也被类和所有实例共有
class Student(object):
count = 0
books = []
'''
"count" "books" "name"和"age"都被称为类的数据属性,但是它们又分为类数据属性和实例数据属性
'''
def __init__(self, name, age): # 类的初始化函数"__init__"
self.name = name
self.age = age
pass
# 1、类数据属性
Student.books.extend(['python', 'javascript'])
print 'Student book list: %s' % Student.books
# Student book list: ['python', 'javascript']
# class can add class attribute after class define
Student.hobbies = ['reading', 'jogging', 'swimming']
print 'Student hobbies list: %s' % Student.hobbies
# Student hobbies list: ['reading', 'jogging', 'swimming']
print dir(Student)
'''
['__class__', '__delattr__', '__dict__', '__doc__', '__format__',
'__getattribute__', '__hash__', '__init__', '__module__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__', 'books', 'count', 'hobbies']
'''
3.2 实例数据属性
- 实例数据属性只能通过实例访问
- 在实例生成后,还可以动态添加实例数据属性,但是这些实例数据属性只属于该实例
class Student(object):
count = 0
books = []
'''
"count" "books" "name"和"age"都被称为类的数据属性,但是它们又分为类数据属性和实例数据属性
'''
def __init__(self, name, age): # 类的初始化函数"__init__"
self.name = name
self.age = age
pass
# 2、实例数据属性
wilber = Student('wilber', 28)
print '%s is %d years old' % (wilber.name, wilber.age)
# wilber is 28 years old
# class instance can add new attribute
# "gender" is the instance attribute only belongs to wilber
wilber.gender = 'male'
print '%s is %s' % (wilber.name, wilber.gender)
# class instance can access class attribute
print dir(wilber)
wilber.books.append('C#')
print wilber.books
will = Student("Will", 27)
print "%s is %d years old" % (will.name, will.age)
# will shares the same class attribute with wilber
# will don't have the "gender" attribute that belongs to wilber
print dir(will)
print will.books
4. 类的方法
-
类方法
类方法使用
@classmethod
指令来声明; -
类实例方法
实例方法则无需使用指令来说明;实例方法的第一个参数必须是
self
,实例方法只能通过类实例进行调用,这时候self
就代表这个类实例本身。通过self
可以直接访问实例的属性; -
类静态方法
与实例方法和类方法不同,静态方法没有参数限制,既不需要实例参数,也不需要类参数,定义的时候使用@staticmethod
装饰器,同类方法一样,静态法可以通过类名访问,也可以通过实例访问。
这三种方法的主要区别在于参数,
- 实例方法被绑定到一个实例,只能通过实例进行调用;
- 但是对于静态方法和类方法,可以通过类名和实例两种方式进行调用;
class MyClass():
def __init__(self):
pass
def aa(self, message): # 类实例方法
print "执行实例方法 aa(%s,%s)" %(self,message)
@classmethod
def class_method(cls, message): # 类方法
print "执行类方法 class_method(%s,%s)" %(cls,message)
@staticmethod
def static_method(): # 类静态方法
print "我是被定义的静态方法"
if __name__ == "__main__":
qq = MyClass()
qq.aa('hello')
# My_object.aa('hello') 类实例方法aa只能被类实例调用,不能被类调用
# 类方法和静态方法可以直接被类和类实例调用
qq.class_method('hello')
MyClass.class_method('hello')
qq.static_method()
MyClass.static_method()
为什么要用到静态方法呢? 如下示例说明:
静态方法用到的场景就是和类相关的操作,但是又不会依赖和改变类、实例的状态,比如经常有跟类有关系的函数,我们希望它在运行时又不需要实例和类参与的情况下直接就能调用。调用静态方法可以无需创建对象。
class Robot(object):
def __init__(self, data):
self.data = data
@staticmethod
def is_need():
return True
def reboot(self):
if Robot.is_need(): # 调用类中的静态方法
print "The pc will reboot for {0}".format(self.data)
def restore(self):
if Robot.is_need():
print "The pc will restort for {0}".format(self.data)
robot1 = Robot("cmd")
robot1.reboot()
robot1.reboot()
类方法的示例
In [15]: class Student(object):
...: num_student=0
...: def __init__(self):
...: Student.num_student+=1
...:
...: @classmethod
...: def get_num_of_instance(cls):
...: return cls.num_student
...:
In [16]: s1=Student()
In [17]: s1.get_num_of_instance()
Out[17]: 1
In [18]: s2=Student()
In [19]: s2.get_num_of_instance()
Out[19]: 2
In [20]: Student.get_num_of_instance()
Out[20]: 2
In [21]: s3=Student()
In [22]: Student.get_num_of_instance()
Out[22]: 3
在 Python
中,静态方法和类方法都是可以通过类对象和类对象实例访问。classmethod
和 staticmethod
的区别是:
classmethod
是类方法,而staticmethod
是静态方法;@classmethod
是一个函数修饰符,它表示接下来的是一个类方法,类方法的第一个参数cls
;- 而实例方法的第一个参数是
self
,表示该类的一个实例;
普通对象方法至少需要一个 self
参数,代表类对象实例,类方法有类变量 cls
传入,从而可以用 cls
做一些相关的处理。并且有子类继承时,调用该类方法时,传入的类变量 cls
是子类,而非父类。
对于类方法,可以通过类来调用,比如说 A
是一个类,那么我们可以通过 A.method()
来调用 A
里面的 method
方法, 也可以通过类的一个实例来调用,如 A().method()
进行调用,首先 A()
方法会调用 A
的初始化方法进行实例化出一个 A
的对象,然后通过该对象调用 method
方法。
静态方法则没有上述方法,它基本上跟一个全局函数相同,一般来说用的很少。
5. 类的继承
class 派生类名(基类名):
类体
Python
中继承中的一些特点:
- 在继承中基类的构造(
__init__()
方法)不会被自动调用,它需要在其派生类的构造中亲自专门调用。 - 在调用基类的方法时,需要使用
super()
前缀。 Python
总是首先查找对应类型的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找(先在本类中查找调用的方法,找不到才去基类中找)。
继承是指一个对象直接使用另一个对象的属性和方法,当一个类拥有另一个类的所有数据和操作时,就称这两个类之间具有继承关系,被继承的类称为父类或者超类,继承了父类或者超类的所有数据和操作的类称为子类。
继承类语法:
class class_name(fatherclass_name)
fatherclass_name
代表的是 class_name
要继承的类 。
如何调用父类的方法:使用类名访问父类中的方法,并在参数列表中引入对象 self
。
class Father():
def __init__(self):
print '我是初始化Father类中的方法'
print '供以后调用'
class Son(Father):
def __init__(self):
print '我是初始化Son类中的方法'
Father.__init__(self) # 使用类名访问父类中的方法,并在参数列表中引入对象self
child = Son()
下面看一个例子,我们以学校的老师和学生为例,老师和学生有共同的特征:姓名、年龄、地址、喜好,不同的是老师有工资,学生有成绩。
我们将学校成员归为一个共同的类。
class SchoolMember(object):
def __init__(self, name, age, addr, hoppy):
self.name = name
self.age = age
self.addr = addr
self.hoppy = hoppy
print '初始化的名字是%s' %self.name
def tell(self):
print '姓名:%s,年龄:%s,地址:%s,喜好:%s' %(self.name,self.age,self.addr,self.hoppy)
Teacher
类继承 SchoolMember
类,采用新式类继承写法时,super()
会带两个参数,第一个是子类的类名,第二个是 self
参数。
super()
可以避免一些类继承的潜在问题,特别是在多重继承上。
class Teacher(SchoolMember):
def __init__(self, name, age, addr, hoppy, salary):
# 使用类名访问父类中的方法,并在参数列表中引入对象self
SchoolMember.__init__(self, name, age, addr, hoppy)
# 新式类继承写法
# super(Teacher, self).__init__(name, age, addr, hoppy)
# 或者
# super().__init__(name, age, addr, hoppy)
self.salary = salary # 添加salary属性
print '我是继承SchoolMember的老师%s' %(self.name)
def tell(self):
SchoolMember.tell(self)
# 或者
# super.tell()
print '我这次的工资是 %s' %self.salary
Student
类继承 SchoolMember
类
class Student(SchoolMember):
def __init__(self, name, age, addr, hoppy, marks):
SchoolMember.__init__(self, name, age, addr, hoppy)
# 新式类继承写法
# super(Student, self).__init__(name, age, addr, hoppy)
# 或者
# super().__init__(name, age, addr, hoppy)
self.marks = marks # 添加marks属性
print '我是继承SchoolMember的学生%s' %(self.name)
def tell(self):
SchoolMember.tell(self)
# 或者
# super.tell()
print '我这次的成绩是 %s' %self.marks
# 分别创建Teacher和Student的实例
t = Teacher('Tom', 40, '陕西西安', '篮球',3000)
s = Student('jack', 14, '北京', '足球', 90)
members = (t, s)
for member in members:
member.tell()
'''
程序输出结果如下:
我是初始化Son类中的方法
我是初始化Father类中的方法
供以后调用
初始化的名字是Tom
我是继承SchoolMember的老师Tom
初始化的名字是jick
我是继承SchoolMember的学生jick
姓名:Tom,年龄:40,地址:陕西西安,喜好:篮球
我这次的工资是 3000
姓名:jack,年龄:14,地址:北京,喜好:足球
我这次的成绩是 90
'''
6. 类的多重继承
子类继承多个父类的方法:
class class_name(fatherclass_name,fatherclass_name1,......):
其中,class_name
为类名,fatherclass_name
,fatherclass_name1
为父类名。
class MyFather():
def __init__(self):
self.eye = '爸爸的眼睛是双眼皮'
print self.eye
class MyMother():
def __init__(self):
self.forehead = '妈妈的额头有点宽'
print self.forehead
class MyAunt():
def __init__(self):
self.nose = '姑姑的鼻子是高鼻梁'
print self.nose
class MySelf(MyFather, MyMother, MyAunt):
def __init__(self, face):
print '我的眼睛是双眼皮,别人说我是继承的是:'
MyFather.__init__(self)
print '我的额头有点宽,别人说我是继承的是:'
MyMother.__init__(self)
print '我的鼻子有点宽,别人说我是继承的是:'
MyAunt.__init__(self)
self.face = face
print '我的脸型:%s' %self.face,'这下终于没人说我像谁了'
me = MySelf('偏圆吧')
7. 通过对象修改类属性
通过对象可以修改类属性值。但这是危险的。类属性被所有同一类及其子类的对象共享。类属性值的改变会影响所有的对象。
class Human(object):
Age = 0
Name = ["Li", "Lei"]
a = Human()
b = Human()
a.Age += 1
print a.Age
print b.Age # b.Age不会改变 Age是immutable型
a.Name[0] = "Wang"
print a.Name
print b.Name # b.Name会改变 Name是mutable型
'''
程序输出结果如下:
1
0
['Wang', 'Lei']
['Wang', 'Lei']
'''
为什么 immutable
变量是可行的呢?
原因是,在更改对象属性时,如果属性是 immutable
的,该属性会被复制出一个副本,存放在对象的__dict__
中。你可以通过下面的方式查看:
print a.__class__.__dict__
print a.__dict__
注意到类中和对象中各有一个 Age
。一个为 0
, 一个为 1
。所以我们在查找 a.Age
的时候,会先查到对象的 __dict__
的值,也就是 1。
但 mutable
的类属性,在更改属性值时,并不会有新的副本。所以更改会被所有的对象看到。
8. 类中的方法调用其它方法
class Human(object):
laugh = 'hahahahaha'
def show_laugh(self):
print self.laugh
def laugh_10th(self):
for i in range(10):
self.show_laugh()
li_lei = Human()
li_lei.laugh_100th()
#类属性laugh ,在方法show_laugh()中,通过self.laugh,调用了该属性的值。
#还可以用相同的方式调用其它方法。方法show_laugh(),在方法laugh_100th中()被调用