@Author : Roger TX ([email protected])
@Link : https://github.com/paotong999
一、面向对象编程思想
面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。
在Python中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类(Class)的概念。
二、类与对象(实例)
在面向对象的程序设计过程中有两个重要概念:类(class)和对象(object,也被称为实例,instance)
- 类是某一批对象的抽象,可以把类理解成某种概念
- 对象是根据类创建出来的一个个具体的“对象”,一个具体存在的实体
Python定义类的简单语法如下:
class 类名(object):
执行语句...
零个到多个类变量...
零个到多个方法...
Python 的语法要求:如果从程序的可读性方面来看,Python 的类名必须是由一个或多个有意义的单词连缀而成的,每个单词首字母大写,其他字母全部小写,单词与单词之间不要使用任何分隔符。
需要说明的是:
- 类中各成员之间的定义顺序没有任何影响,各成员之间可以相互调用。
-
(object)
表示该类是从哪个类继承下来的,继承的概念我们后面再讲,通常,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。
对象(实例)的创建和使用:
class Person :
'这是一个学习Python定义的一个Person类'
# 下面定义了一个类变量
hair = 'black'
def __init__(self, name = 'Charlie', age=8):
# 下面为Person对象增加2个实例变量
self.name = name
self.age = age
def say(self, content):
# 下面定义了一个say方法
print(content)
print(self.age)
f = Person()
f.__init__(age=11) # 等同于Person(age=11)
f.say('123')
- 创建实例可以通过类名+()
Person()
实现 - 创建对象时默认调用构造方法,可以传入参数,初始化实例变量
-
__init__
构造函数只能return None
Python 类所包含的最重要的两个成员就是变量和方法:
- 类变量属于类本身,用于定义该类本身所包含的状态数据
- 实例变量则属于该类的实例对象,用于定义实例对象所包含的状态数据
- 方法则用于定义该类的对象的行为或功能实现
在类中定义的方法默认是实例方法,实例方法至少应该定义一个参数,该参数通常会被命名为 self。
- 定义实例方法的方法与定义函数的方法基本相同,只是实例方法的第一个参数会被绑定到方法的调用者(该类的实例)。
- 类所包含的类变量可以动态增加或删除(程序在类体中为新变量赋值就是增加类变量),程序也可在任何地方为已有的类增加变量;程序可通过 del 语句删除己有类的类变量。
- 实例对象的实例变量也可以动态增加或删除(只要对新实例变量赋值就是增加实例变量),因此程序可以在任何地方为己有的对象增加实例变量;程序可通过 del 语句删除已有对象的实例变量。
使用 __slots__
限制实例的属性
Python允许在定义class的时候,定义一个特殊的 __slots__
变量,来限制该class实例能添加的属性
class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
- 由于'score'没有被放到
__slots__
中,所以不能绑定score属性,试图绑定score将得到AttributeError的错误 - 使用
__slots__
要注意,__slots__
定义的属性仅对当前类实例起作用,对继承的子类是不起作用的
三、变量与方法
构造方法
在实例方法中有一个特别的方法:
__init__
,这个方法被称为构造方法。构造方法用于构造该类的对象,Python 通过调用构造方法返回该类的对象(无须使用 new)。构造方法是一个类创建对象的根本途径,因此 Python 还提供了一个功能:如果开发者没有为该类定义任何构造方法,那么 Python 会自动为该类定义一个只包含一个 self 参数的默认的构造方法。
方法的调用
对象访问方法或变量的语法是:
对象.变量|方法(参数)
。在这种方式中,对象是主调者,用于访问该对象的变量或方法。在使用类调用实例方法时,Python 不会自动为第一个参数绑定调用者。实际上也没法自动绑定,因此实例方法的调用者是类本身,而不是对象。
如果程序依然希望使用类来调用实例方法,则必须手动为方法的每一个参数传入参数值。
类变量和实例变量
通常来说,实例变量是对于每个实例都独有的数据,而类变量是该类所有实例共享的属性和方法。
- 类变量又叫全局变量,是属于类的特性,实例先找实例化变量,然后再去找类变量
- 类变量可以用实例去调用
- 如果类变量有多重继承关系, 就需要按照指定的路线进行查找
下面代码定义了一个 Address 类,并为该类定义了多个类变量:
class Address :
detail = '广州'
def __init__(self, detail):
# 尝试直接访问类变量
#print(detail) # 报错
# 通过类来访问类变量
print(Address.detail) # 输出 广州
# 通过类来访问Address类的类变量
print(Address.detail)
addr = Address('广州')
# 修改addr实例的变量
addr.detail = '佛山'
print(Address.detail) # 输出 广州
print(addr.detail) # 输出 佛山
- 因为动态语言的特性,
addr.detail = '佛山'
并没有改变类变量,只是新增了一个实例变量 - 在实例方法内部,可以通过
类名.类变量
来访问类变量,还可以使用self__class__.类变量
来访问类变量
四、实例方法与类方法、静态方法
实例方法
普通实例方法,第一个参数需要是 self
,它表示一个具体的实例本身。
class Date:
def __init__(self,year,month,day):
self.year=year
self.month=month
self.day=day
def __str__(self):
return ("{year}-{month}-{day}").format(year=self.year,month=self.month,day=self.day)
def yesterday(self):
self.day-=1
date=Date(2018,10,20)
print(date)
date.yesterday()
print(date)
类方法
在一个方法的上面,加上装饰器 @classmethod
就表示这个方法是类方法。
对于类方法,它的第一个参数不是self,是cls,它表示这个类本身。
静态方法
在一个方法的上面,加上装饰器 @staticmethod
就表示这个方法是静态方法。
静态方法对参数没有要求,可以不传。
class Foo(object):
X = 1
Y = 14
@staticmethod
def averag(*mixes):
# "父类中的静态方法"
return sum(mixes) / len(mixes)
@staticmethod
def static_method():
# "父类中的静态方法"
print ("父类中的静态方法")
return Foo.averag(Foo.X, Foo.Y)
@classmethod
def class_method(cls):
# 父类中的类方法
print ("父类中的类方法" )
return cls.averag(cls.X, cls.Y)
class Son(Foo):
X = 3
Y = 5
@staticmethod
def averag(*mixes):
# "子类中重载了父类的静态方法"
print ("子类中重载了父类的静态方法")
return sum(mixes) / 3
p = Son()
print (p.averag(1,5)) # 最后一段,结果为2.0
print (p.static_method()) # static_method段,结果为7.5
print (p.class_method()) # class_method段,最后一段,结果为2.6
五、成员可见性
成员的可见性
- 公开的,外部可以访问的,public。
- 私有的,外部不可访问的,private。
可见性关键字
- 其它语言中使用public和private关键字,python中没有
- python中使用
__
双下划线区分公开和私有,__
表示成员是私有的
class Student():
sum1 = 1
def __init__(self,name,age):
self.name = name
self.age = age
self.score = 0 #增加变量score,并给他初始值
def __marking(self,score):
#加双下划线,变为私有,在外部不能引用
if score < 0:
return "该同学的成绩不正常"
self.score = score
print(self.name,"同学本次考试的成绩为:",self.score)
student = Student('李华',18)
result = student.__marking(10)
print(result)
执行后 'Student' object has no attribute '__marking'
错误,因为 __marking
对于 Student
不可见
通过 student.__dict__
以及 dir(student)
两个内置的函数可以查看到stu对象的所有数据成员和方法
{'name': '李华', 'age': 18, 'score': 0}
['_Student__marking', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__',
'__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__', 'age', 'name', 'score', 'sum1']
可以看到原本的 __marking
变成了 _Student__marking
访问私有成员
class Address :
def __init__(self, detail):
self.__detail = detail
addr = Address('广州')
addr.__detail = '佛山'
print(addr.__dict__) # {'_Address__detail': '广州', '__detail': '佛山'}
print(addr._Address__detail) # 广州
私有变量可以使用 _Address__detail
进行强制访问
私有变量不可见,但是对其进行赋值操作,并不会报错,这是因为Python动态语言的特性, 赋值只是新增了一个实例变量,并没有改变原有的私有变量