引子
假设你现在是一个生产各种动物玩具工厂的厂长,现在有家订货商急需一批小狗玩具,于是你和工程师们连夜设计小狗模具,终于在一周后设计好了模具,并开始生产。没过多久,又有家订货商急需一批小猫的玩具,于是你和工程师们又连夜设计,终于在一周后设计好了猫模具,并开始生产。于是,你开始想,每次从头开始设计模具有点太花时间了,而且你发现小狗和小猫的某些部位是可以通用的,那么能不能制造一个通用模具,比如身体、尾巴、腿等,这样每次设计新的动物模具时,只需设计每种动物特有的部位就可以了,其他的部位可以使用通用部分,最后组装起来就可以了。这样的话,省时省力,又可以满足要求。但是新的问题又出现了,有家订货商需要一批鲨鱼玩具,你发现鲨鱼和猫、狗的玩具几乎没有通用的部分,但是和鲤鱼、鲫鱼等有相似的部分,于是你又想到,可以把这些动物进行分类,把猫、狗、猪、狮子等陆地哺乳类动物列为一类,它们可以共享一部分相同的部位,而把鲤鱼、鲨鱼等鱼类列为一类,它们可以共享一部分相同的部位。这样,我们可以设计不同类动物的模具,生产通用的部位,然后根据每种动物的特点选择不同类的模具,接着在设计每种动物的特有部分,最后组装起来就可以了。
由此我们得到一个“类”的概念。类,可以通俗地理解为一类相似对象的统称或集合,为一个抽象概念,即对象的类型。程序设计中的“类”,相当于一个通用模具,类的组成就是对该类相似对象的一个描述。对象的描述主要从两方面进行刻画,一是对象的特征(属性),二是对象的行为(方法)。创建类就创建了一个相似对象的模具,实例化就是利用该模具定制了一个具体的对象(物品)。
Python是一种面向对象编程(OOP)的语言。所谓的面向对象编程就是把对象作为程序的基本单元,一个对象包含数据特征和操作数据的函数。在Python中,所有数据类型都被视为对象,也可以自定义对象。自定义对象数据类型就是面向对象中类的概念。
下面,我们将就Python中“类”的概念进行详细说明。
1 类的构成
- (1)类由三个部分构成 - 类名称:类型 - 类属性:对象的属性 - 类方法:对象的方法
- (2)创建和使用类,类定义形式(代码): class 类名:
属性列表:
方法列表:
- (3)类是对象的类型,具有相同属性和行为事物的统称。类是抽象的,在使用的时候需要定义类的一个具体存在,即实例化对象。
# 定义类
class People:
# 类属性
sex = 'nan'
# 构造函数:魔术方法
def __init__(self, name, age):
# 实例化属性
self.name = name # self代表对象本身
self.age = age
# 实例化方法
def sleep(self):
self.aa = 1
print('{}正在睡觉,性别为{}'.format(self.name, People.sex))
def bb(self):
print(self.aa)
# 实例化对象
people1 = People('张三', 21) # 属性赋予实例化对象
People.sex='男'
people1.sleep() # 调用实例化对象方法
print(people1.age) # 可以直接打印实例化对象的属性
输出结果:
张三正在睡觉,性别为男
21
2 类的属性与实例属性
- (1)类变量:类变量在整个实例化的对象中是公用的。
- (2)类变量定义在类中且在函数体之外
- (3)类变量通常不作为实例变量使用。类属性使用类变量表示的,在类的外部使用,需用类名.类属性。
直观地,类属性很好理解,类本质上就是如果我们设计的某一类物品的一个模具,类属性是这一类事物所共有的特征(属性)。
class PeopleMan:
# 定义基本类属性
name = ''
age = 0
sex = 'woman'
__weight = 0
def __init__(self, n, a, w):
# 定义实例属性
self.name = n
self.age = a
self.__weigth = w
def speak(self):
print("%s 说:我%d 岁。" %(self.name, self.age))
p = PeopleMan('runoob', 10, 30) # 实例化类
print(PeopleMan.sex) # 类名.类变量 调用类变量
输出结果:
woman
- (4)实例属性:定义在方法中的变量,只作用于当前实例的类
- (5)变量名 为私有属性,即该类所私有的属性,普通调用方式无法调用。如果要在类外部调用,需要用 对象名._类名__私有属性 这样才能调用。注意类名前是单下划线,类名后是双下划线。
class PeopleMan:
# 定义基本类属性
name = ''
age = 0
sex = 'woman'
# 定义私有属性,私有属性在类外部无法直接进行访问
__weight = 0
def __init__(self, n, a, w):
# 定义实例属性
self.name = n
self.age = a
self.__weight = w
def speak(self):
print("%s 说:我 %d 岁。" % (self.name, self.age))
# 实例化类
p = PeopleMan('runoob', 10, 30)
print(p._PeopleMan__weight) # 对象名.__类名__私有属性 这样才能调用p.__weight,否则无法调用
p.speak()
输出结果:
30
runoob 说:我 10 岁。
- (6)可以实例化对象名+.来访问对象的属性,也可以使用一下函数的方式来访问属性(反射)
- getattr(obj, name[,default]): 访问对象的属性
- hasattr(obj, name): 检查是否存在一个属性
- setattr(obj, name, value): 设置一个属性。如果属性不存在,会创建一个新属性
- delattr(obj, name): 删除属性值,没有删除属性
- name 需加单引号,obj为实例化对象名称
class PeopleMan:
# 定义基本类属性
name = ''
age = 0
sex = 'woman'
# 定义私有属性,私有属性在类外部无法直接进行访问
__weight = 0
def __init__(self, n, a, w):
# 定义实例属性
self.name = n
self.age = a
self.__weight = w
def speak(self):
print("%s 说:我 %d 岁。" %(self.name, self.age))
# 实例化
p = PeopleMan('runoob', 10, 30) # p为实例化对象
getattr(p, 'sex') # 访问对象的属性
输出结果:
'woman'
- (7)__dict__: 类的属性(包括一个字典,由类的属性名:值组成)实现方式:实例化类名.dict
- (8)__doc__: 类的文档的字符串 实现方式:(类名.)实例化类名.doc
- (9)__name__: 类名 实现方式:类名.name
- (10)__bases__: 类的所有父类构成元素(包含了一个由所有父类组成的元组
class PeopleMan:
'''定义人类,其中有3个属性,两个方法'''
# 定义基本类属性
name = ''
age = 0
sex = 'woman'
# 定义私有属性,私有属性在类外部无法直接进行访问
__weight = 0
def __init__(self, n, a, w):
# 定义实例属性
self.name = n
self.age = a
self.__weight = w
def speak(self):
print("%s 说:我 %d 岁。" %(self.name, self.age))
# 实例化类
p = PeopleMan('runoob', 10, 30) # p为实例化对象
print(getattr(p, 'sex')) # 访问对象的属性
print(p.__dict__)
print(PeopleMan.__dict__)
print(p.__doc__) # 类文档中的字符串
print(PeopleMan.__doc__) # 类文档中的字符串
print(PeopleMan.__name__)
输出结果:
woman
{'name': 'runoob', 'age': 10, '_PeopleMan__weight': 30}
{'__module__': '__main__', '__doc__': '定义人类,其中有3个属性,两个方法', 'name': '', 'age': 0, 'sex': 'woman', '_PeopleMan__weight': 0, '__init__': <function PeopleMan.__init__ at 0x000002C3EAFAF5E0>, 'speak': <function PeopleMan.speak at 0x000002C3EAFAF700>, '__dict__': <attribute '__dict__' of 'PeopleMan' objects>, '__weakref__': <attribute '__weakref__' of 'PeopleMan' objects>}
定义人类,其中有3个属性,两个方法
定义人类,其中有3个属性,两个方法
PeopleMan
- (11)尽量把需要用户传入的属性作为实例属性,而把同类都有的属性作为类属性
- (12)实例属性在每实例化一个类时都会初始化一遍
- (13)不同的实例的实例属性可能不同,不同实例的类属性都相同
- (14)实例属性
- 在__init__(self,...)中初始化
- 内部调用时都需要加上self
- 外部调用时用“对象名.属性名”调用
- (15)类属性
- 类属性可以在__init__中修改
- 在内部用类名.类属性名调用
- 外部既可以用类名.类属性名又可以用instancename.类属性名调用
- (16)私有属性
- 双下划线__开头:外部不可通过“对象名.属性名”来访问或者更改,可以通过“对象._类名__属性名”访问和更改
class PeopleMan:
# 定义基本类属性
name = ''
age = 0
sex = 'woman'
# 定义私有属性,私有属性在类外部无法直接进行访问
__weight = 0
def __init__(self, n, a, w):
# 定义实例属性
self.name = n
self.age = a
self.__weight = w
def speak(self):
print("%s 说:我 %d 岁。" %(self.name, self.age))
# 实例化类
p = PeopleMan('runoob', 10, 30)
p._PeopleMan__weight = 50
print(p._PeopleMan__weight)
数据结果:
50
3 __init__()方法和self
- (1)__init__()是一个特殊的方法属于类的专有方法,被称为类的构造函数或初始化方法,方法的前面和后面都有两个下划线,这时为了避免Python默认方法和普通方法发生名称的冲突。
- (2)每当创建类的实例化对象的时候,__init__()方法都会默认被运行,作用就是初始化已实例化的对象。
- (3)在方法的定义中,第一个参数self是必不可少的。类的方法和普通的函数区别就是self,self并不使Python关键字,完全可以用其他单词取代它,只是按照惯例和标准的规定,推荐使用self。
class PeopleMan:
# 定义基本类属性
name = ''
age = 0
sex = 'woman'
# 定义私有属性,私有属性在类外部无法直接进行访问
__weight = 0
def __init__(r, n, a, w): # self换成r
# 定义实例属性
r.name = n
r.age = a
r.__weight = w
def speak(self): # 这个self没有换,它和上面的r一样,都是实例化对象p,该方法里的所有self也可以换成r
print("%s 说:我 %d 岁。" %(self.name, self.age))
# 实例化类
p = PeopleMan('runoob', 10, 30) # 创建实例化对象,p是实例化对象,默认运行__init__()
getattr(p, 'sex') # 访问对象的属性
'woman'
- (4)在类中方法里面,不写__init__()构造方法,直接写实例化方法,或者实例化方法里面初始化实例属性,当类比较小或只构造一个类,然后实例化方法通过实例化对象的参数传进去好像比较简单、快捷,并且不会报错,但如果类里面的多个方法需要这个参数,那么就需要传多次,比较繁琐。
- (5)如果通过构造方法,在第一次实例化对象的时候传入,实例化可以直接调用实例化对象中的self.属性,不需要在调用实例化方法的时候传入属性,工程庞大的话,用__init__()方法然后self调用方便些,也规范些。
class PeopleMan:
# 定义基本类属性
name = ''
age = 0
sex = 'woman'
# 定义私有属性,私有属性在类外部无法直接进行访问
__weight = 0
def speak(self, n, a): # 这个self没有换,它和上面的r一样,都是实例化对象p,该方法里的所有self也可以换成r
self.name = n
self.age = a
print("%s 说:我 %d 岁。" %(self.name, self.age))
def run(self, n, a): # 这个self没有换,它和上面的r一样,都是实例化对象p,该方法里的所有self也可以换成r
self.name = n
self.age = a
print("%s 说:我 %d 岁。" %(self.name, self.age))
# 实例化类
p = PeopleMan() # 创建实例化对象,p是实例化对象,默认运行__init__()
p.speak('runoob', 10) # 调用方法时传入
p.run('runnoob', 10) # 由于实例属性没有初始化,因而如果再次调用,就需要再传入参数
runoob 说:我 10 岁。
runnoob 说:我 10 岁。
4 类的三大特性
4.1 继承
- (1)现实生活中,继承一般指子女继承父辈的财产。程序中,继承描述的是事物之间的所属关系,例如,猫和狗都属于动物,程序中可以描述为猫和狗都继承自动物。同理,波斯猫和家猫都继承猫。而斑点狗,泰迪都继承狗。
- (2)程序中我们定义一个class的时候,可以从某个现有的class继承,新的class称为之子类(subclass),而被继承的class称之为基类、父类或超类。
- (3)子类继承其父类的所有属性和方法,同时还可以定义自己的属性和方法。
- (4)可以多重继承,但是最好只写一个基类,需要注意圆括号基类的顺序,若是基类中有相同的方法名,而在子类使用时未指定,python从左至右搜索,即方法在子类中未找到时,从左至右查找基类中是否包含方法。
继承的形式:
class DerivedClassName(BaseClassName1, BaseClassName2, BaseClassName3):
......
- (5)继承会继承父类的所有属性,如果不想被子类继承,就把属性设置为私有属性,子类无法继承父类的私有属性,但是可以访问。通常父类的基本属性对子类继承是没有影响的,通过实例化属性都是放在方法中,子类可以通过方法重写来实现对继承的属性修改。
class PeopleMan:
# 定义基本属性
name = ''
age = 0
sex = 'woman'
# 定义私有属性,私有属性在类外部无法直接进行访问
__weight = 0
def __init__(self, n, a, w):
# 定义实例属性
self.name = n
self.age = a
self.__weight = w
def speak(self):
print("%s 说:我 %d 岁。" %(self.name, self.age))
class speaker:
topic = ''
name = ''
def __init__(self, n, t):
self.name = n
self.topic = t
def speak(self):
print("我叫 %s: 我是一个演说家,我演讲的主题是 %s" %(self.name, self.topic))
class student(PeopleMan, speaker): # student类拥有基类的属性和方法
grade = ''
def __init__(self, n, a, w, g, t):
# 调用父类的构造函数
PeopleMan.__init__(self, n, a, w) # 调用父类构造方法,或者super(student, self).__init__(n, a, w) 要先传入第一个基类的参数
speaker.__init__(self, n, t) # 和父类实例化属性保持一致 或者super(student, self).__init__(n, t)
self.grade = g
def speak(self): # 重写父类的方法,相当于对父类的方法重新定义
print("%s 说:我 %d 岁了,我在读%d年级"%(self.name, self.age, self.grade)) # 子类可以用父类的属性
s = student('ken', 10, 60, 3, "Python")
s.speak() # 先找子类,子类没有则从左至右基类中找方法运行结果
输出结果:
ken 说:我 10 岁了,我在读3年级
4.2 多态
- (1)它是指对不同类型的变量进行相同的操作,它会根据对象(或类)类型的不同而表现出不同的行为。
- (2)多态是指一类事物有多种形态,比如动物类,可以有猫、狗、猪等等。(一个抽象类有多个子类),因而多态的概念依赖于继承)。
import abc
class Animal(metaclass=abc.ABCMeta): # 同一类事物:动物
@abc.abstractmethod
def talk(self):
pass
class Cat(Animal): # 动物的形态之一:猫
def talk(self):
print('say miaomaio')
class Dog(Animal): # 动物的形态之二:狗
def talk(self):
print('say wangwang')
class Pig(Animal): # 动物的形态之三:猪
def talk(self):
print("say aoao")
- (3)多态与多态性是两种概念,多态性是指具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容的函数。在面向对象方法中一般是这样表述多态性:向不同的对象发送同一条消息,不同的对象在接受时会产生不同的行为(即方法)。也就是说,每个对象可以用自己的方式响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。
- (4)综合可以说,多态性是:一个接口,多种实现。
# 多态性
import abc
class Animal(metaclass=abc.ABCMeta): #同一类事物:动物
@abc.abstractmethod
def talk(self):
pass
class Cat(Animal):
def talk(self):
print('say miaomiao') # 多态性
class Dog(Animal):
def talk(self):
print('say wangwang') # 多态性
class Pig(Animal):
def talk(self):
print('say aoao') # 多态性
c = Cat()
d = Dog()
p = Pig()
def func(obj):
obj.talk()
func(c) # 一个接口,多种实现
func(d) # 一个接口,多种实现
func(p) # 一个接口,多种实现
输出结果:
say miaomiao
say wangwang
say aoao
4.3 封装性
- (1)“封装”就是将对象的特征(属性)和行为(方法)封装在一起,形成一类对象,即类;封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只要通过外部接口,即特定的访问权限来使用类的成员。
5 类中的方法
Python中有三种方法,实例方法、静态方法(staticmethod)和类方法(classmethod)
Class A():
method = 'class'
# 实例方法
def normethod(self):
print('I am the normal method')
# 静态方法
@staticmethod
def stamethod():
print(' I am the static method')
# 类方法
def clsmethod(cls):
print(f' I am the {cls.method} method')
5.1 实例化方法
实例方法第一个参数是self,它表示实例化后类的地址id,所以需要一个类的实例,才能调用这个函数。
案例一:
A.normthod() # 直接这样调用会报错
# 必须先进行实例化
a = A()
a.normethod() # 需要实例化后传递id参数才能调用
# 输出:I am the normal method
案例二:
class A:
# 基本属性
d = 0
# 实例化方法
def foo(self, x):
print("executing foo(%s, %s)" %(self, x)) # 打印self, x分别是什么
print("self: ", self) # 打印self是什么
# 方法一:直接调用类的实例化方法
a = A.foo('a', 'b') # self被作为一个参数传入了,self就是一个参数而已,并不是一个对象,因此不能打印self.x
# 方法二:实例化方法
b = A() #需要用实例化对象调用它
b.foo('a') # 打印出来的self为实例化对象的地址
输出结果:
executing foo(a, b)
self: a
executing foo(<__main__.A object at 0x000002C3EB3579A0>, a)
self: <__main__.A object at 0x000002C3EB3579A0>
5.2 静态方法
静态方法类似普通函数,参数里面不用传递self。有一些方法和类相关,但是又不需要类中的任何信息,出于对代码的理解和维护,就可使用静态方法。总结如下:
- (1)通过装饰器@staticmethod装饰
- (2)不能访问实例属性
- (3)参数不能传入self
- (4)与类相关但是不依赖类与实例的方法
- (5)静态方法不需要接受参数,使用类名.类属性
案例一:
A.stamethod() # 静态方法不用实例可直接调用
# 输出: I am the static method
a = A()
a.stamethod() # 也可以实例化后再调用
# 输出:I am the static method
案例二:
class A:
# 基本类属性
d = 0
# 静态方法
@staticmethod
def static_foo(x):
print("executing static_foo(%s)" %x)
print(A.d) # 访问类属性,不能访问实例属性
# 静态方法
A.static_foo('a')
输出结果:
executing static_foo(a)
0
静态方法最大的优点是能节省开销,因为它不会绑定到实例对象上,它在内存中只生成一个。
a1 = A()
a2 = A()
a1.stamethod
# 输出:<function static at 0x0000000004938E48>
a2.stamethod
# 输出:<function static at 0x0000000004938E48>
A.stamethod
# 输出:<function static at 0x0000000004938E48>
a1.stamethod is A.stamethod and a2.stamethod is A.stamethod
# 输出:True
而实例方法每个实例对象都是独立的,开销较大。
a1.normethod
# 输出:<bound method A.norstatic of <__main__.A instance at 0x0000000004590AC8>>
a2.normethod
# 输出:<bound method A.norstatic of <__main__.A instance at 0x00000000045901C8>>
a1.normethod is a2.normethod
# 输出:False
5.3 类方法
类方法与实例方法类似,但是类方法传递的不是实例,而是类本身。当需要和类交互而不需要和实例交互时,就可以选择类方法。总结如下:
- (1)在类方法前加装饰器@classmethod
- (2)不能访问实例属性
- (3)参数必须传入cls(即代表类对象--区别--self代表实例对象),并且用此来调用类属性:cls.类属性名
- (4)静态方法和类方法都可以用类或者实例来调用,二者的特点是均不能调用实例属性。
案例一:
A.clsmethod() #类方法不用实例化可直接调用
# 输出:I am the class method
a = A()
a.clsmethod() #也可以实例化后再调用
# 输出:I am the class method
案例二:
class A:
# 基本类属性
d = 0
def __init__(self, n):
self.name = n
# 类方法
@classmethod
def class_foo(cls, x):
print("executing class_foo(%s, %s)" %(cls, x))
print('cls: ', cls) # 打印cls是什么
print(cls.d) # cls为类,cls.d为类属性
# print(cls.n) # 报错,因为n是实例属性,不是类的属性
# 方法一:通过类来调用
A.class_foo('a') # cls是一个类,类方法可以只传入一个参数x来调用
# 方法二:通过实例化对象来调用
b = A("tom")
b.class_foo("a")
输出结果:
executing class_foo(<class '__main__.A'>, a)
cls: <class '__main__.A'>
0
executing class_foo(<class '__main__.A'>, a)
cls: <class '__main__.A'>
0
- (5)当把装饰器cls注释掉,cls不是类,是实例化对象,相当于self
class A:
# 基本类属性
d = 0
# 实例化方法
def foo(self, x):
print("executing foo(%s, %s)" %(self, x))
print("self:", self) # 打印self是什么
# 类方法
# @classmethod
def class_foo(cls, x): # cls相当于self
print("executing class_foo(%s, %s)" %(cls, x))
print('cls: ', cls)
print(cls.d) # d是类变量,类变量通常不作为实例变量使用。如果需要用在函数中使用 类名.类属性,这里用self.d可以用行,但是不规范
# 静态方法
@staticmethod
def static_foo(x):
print("executing static_foo(%s)" %x)
b = A()
b.class_foo("a")
executing class_foo(<__main__.A object at 0x000002C3EB17F520>, a)
cls: <__main__.A object at 0x000002C3EB17F520>
0
class A:
# 基本类属性
d = 0
def __init__(self, m, n):
self.m = m
self.n = n
# 实例化方法
def foo(self, x, y):
# 实例属性,不是只有写在init里面的才是实例属性,只不过该实例属性只能在该方法中使用,其他地方不能调用
self.y = y
print("executing foo(%s, %s)" %(self, x)) # self为实例化对象,x为传入参数的值
print("self: ", self) # 打印self是什么
# 实例化方法里面可以调用类方法
print(self.class_foo(self.m)) # 实例化对象b就是相当于self,只不过self在里面访问,b在外面访问
print(self.class_foo(self.y)) # 运行self.class_foo(self.y)函数,相当于b.class_foo(self.y), b.class_foo(self.y)的返回值为None,所以打印None
print(x)
# print(self.class_foo(self.x)) # 如果前面不加self.x = x,那么x就仅仅只是一个入口参数,不是实例化属性
# 类方法
@classmethod
def class_foo(cls, x):
print("executing class_foo(%s, %s)" %(cls, x))
print('cls:', cls) # 打印cls是什么
print(cls.d) # cls为类,cls.d为类属性
# print(cls.n) #报错,因为n是实例属性,不是类属性
# 静态方法
@staticmethod
def static_foo(x):
print("executing static_foo(%s)" %x)
b = A(10, 2) # __init__初始化需要传入两个参数
b.foo("a", "y") # 实例化对象foo方法需要传入两个参数
输出结果:
executing foo(<__main__.A object at 0x000002C3EA64DAC0>, a)
self: <__main__.A object at 0x000002C3EA64DAC0>
executing class_foo(<class '__main__.A'>, 10)
cls: <class '__main__.A'>
0
None
executing class_foo(<class '__main__.A'>, y)
cls: <class '__main__.A'>
0
None
a
类方法还有一个应用场景就是对类的初始化参数做预先处理,比如类在使用的时候一般初始化参数格式是固定的,而这个时候又需要传入其他格式的参数来初始化,这个时候就可以用到类方法。
看下面定义的一个时间类:
class Data_test(object):
day=0
month=0
year=0
def __init__(self,year=0,month=0,day=0):
self.day=day
self.month=month
self.year=year
def out_date(self):
print "year :"
print self.year
print "month :"
print self.month
print "day :"
print self.day
t=Data_test(2016,8,1)
t.out_date()
输出:
year :
2016
month :
8
day :
1
如果用户输入的是“2016-8-1"这样的字符格式,那么就需要调用Date_test类前做一下处理:
string_date='2016-8-1'
year,month,day=map(int,string_date.split('-'))
s=Data_test(year,month,day)
先把‘2016-8-1’ 分解成 year,month,day 三个变量,然后转成int,再调用Date_test(year,month,day)函数。 也很符合期望。
那我可不可以把这个字符串处理的函数放到 Date_test 类当中呢?那么@classmethod 就开始出场了
class Data_test2(object):
day=0
month=0
year=0
def __init__(self,year=0,month=0,day=0):
self.day=day
self.month=month
self.year=year
@classmethod
def get_date(cls,
string_date):
#这里第一个参数是cls, 表示调用当前的类名
year,month,day=map(int,string_date.split('-'))
date1=cls(year,month,day)
#返回的是一个初始化后的类
return date1
def out_date(self):
print "year :"
print self.year
print "month :"
print self.month
print "day :"
print self.day
在Date_test类里面创建一个成员函数, 前面用了@classmethod装饰。 它的作用就是有点像静态类,比静态类不一样的就是它可以传进来一个当前类作为第一个参数。那如何调用呢?
r=Data_test2.get_date("2016-8-6")
r.out_date()
输出:
year :
2016
month :
8
day :
1
这样子等于先调用get_date()对字符串进行处理,然后才使用Data_test的构造函数初始化。
这样的好处就是你以后重构类的时候不必要修改构造函数,只需要额外添加你要处理的函数,然后使用装饰符 @classmethod 就可以了。
6 方法重写
- (1)避免用下划线作为变量名的开始,因为下划线对解释器有特殊的意义
- (2)双下划线 变量名 双下划线:定义的是特殊方法,类似__init__()之类的
- (3)单下划线 变量名:“单下划线”开始的成员变量叫作保护变量,意思是只有类对象和子类对象自己能访问到这些变量。以单下划线开头的代表不能直接访问的类属性,需要通过类提供的接口进行访问,不能用“from xxx import * ”而导入
- (4)双下划线 变量名:“双下划线”开始的是私有成员,意思是只有类对象自己能访问,连子类对象也不能访问到这个数据
class Parent: # 定义父类
def myMethod(self):
print('调用父类方法')
class Child(Parent): # 定义子类
def myMethod(self):
print('调用子类方法')
c = Child() # 子类实例
c.myMethod() # 子类调用重写方法
super(Child, c).myMethod() # 用子类对象调用父类已被覆盖的方法
调用子类方法
调用父类方法
7 获取对象信息
当我们拿到一个对象的引用时,如何知道这个对象是什么类型、有哪些方法呢?
7.1 使用type()
# 基本类型的判断
print(type(123))
print(type('str'))
print(type(None))
# 对函数或类类型的判断
class Animal(object):
pass
a = Animal()
print(type(abs))
print(type(a))
<class 'int'>
<class 'str'>
<class 'NoneType'>
<class 'builtin_function_or_method'>
<class '__main__.Animal'>
type()函数返回的是Class类型。如果我们要用if语句中判断,就需要比较两个变量的type类型是否相同。
print(type(123) == type(456))
print(type(123) == int)
print(type('abc') == str)
print(type('abc') == type(123))
# 如何判断一个对象是否是函数?可以使用types模块中定义的常量:
import types
def fn():
pass
print(type(fn) == types.FunctionType)
print(type(abs) == types.BuiltinFunctionType)
print(type(lambda x: x) == types.LambdaType)
print(type(x for x in range(10)) == types.GeneratorType)
输出结果:
True
True
True
False
True
True
True
True
7.2 使用instance()
对于class的继承关系来说,使用type()很不方便。因此我们要判断class类型,可以使用isinstance()函数。
class Animal(object):
def run(self):
print('Animal is running')
class Dog(Animal):
def run(self):
print('Dog is running')
a = Animal()
d = Dog()
print(isinstance(a, Animal))
print(isinstance(d, Dog))
print(isinstance(d, Animal)) # Dog是从Animal继承的,所以d对象也是dog
# 判断基本类型
print(isinstance('a', str))
print(isinstance(123, int))
print(isinstance(b'a', bytes))
# 判断一个变量是否是某些类型中的一种
print(isinstance([1, 2, 3], (list, tuple)))
print(isinstance((1, 2, 3), (list, tuple)))
7.3 使用dir()
使用dir()可以获得一个对象所有的属性和方法,它返回一个包含字符串的list,比如,获得一个str对象的所有属性和方法:
dir('ABC')
8 相关定义
- (1)类(class):用来描述具有相同属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例,类对象的类型。类是抽象的概念,而对象是一个你能够摸得着,看得到的实体。两者相辅相成,谁也离不开谁。
- (2)类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
- (3)数据成员:类变量或者实例变量,用于处理类及其实例对象的相关数据。
- (4)方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫覆盖(overide),也称为方法的重写。
- (5)局部变量:定义在方法中的变量,只作用于当前实例的类
- (6)实例变量:在类的声明中,属性使用变量来表示的。这种变量就称为实例变量,是在类声明的内部但是在类的其他成员方法之外声明的。
- (7)继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。
- (8)实例化:创建一个类的实例,类的具体对象
- (9)方法:类中定义的函数。
- (10)对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
9 总结
类是具有相同属性和方法的对象的集合,因此,类的构成也包括属性和方法的两部分。创建类就是定义属性(类属性和实例属性)和方法(实例化方法、静态方法和类方法),然后对创建的类进行实例化并根据需要进行响应的操作。其中,对属性和方法的操作包括自定义、继承、多态、重写、调用等。