面向对象+
静态方法和类方法
静态方法
导入
通过装饰器@staticmethod
来进行装饰。静态方法既不需要传递类对象也不需要传递实例对象
静态方法也可以通过实例对象和类对象去访问
class Dog:
type='狗'
def __init__(self):
name=None
#静态方法
@staticmethod
def introduce():#静态方法不会自动传递实例对象和类对象
print('犬科哺乳动物')
dog=Dog()#实例化
Dog.introduce()
dog.introduce()
#结果
犬科哺乳动物
犬科哺乳动物
如果不添加@staticmethod
,由于introduce没有传入self,会报错。所有静态方法是类中的函数,不需要实例。
静态方法主要是用来存放逻辑性的代码,逻辑上属于类,但和类本身没有关系。也就是说,在静态方法中,
不会涉及类中属性和方法的操作。
即:静态方法是一个独立的,单纯的函数,仅仅是托管与某个类的名称空间中,便于维护和管理
使用场景
- 当方法中 既不需要使用实例对象(如实例对象,实例属性),也不需要使用类对象 (如类属性、类方法、创建实例等)时,定义静态方法
- 取消不需要的参数传递,有利于 减少不必要的内存占用和性能消耗
- 如果在类外面写一个同样的函数来做这些事,打乱了逻辑关系,导致代码维护困难,使用静态方法。
类方法
解释
- 类对象所拥有的方法
- 需要用修饰器
@classmethod
来标识其为类方法 - 对于类方法,第一个参数必须是类对象,一般以
cls
作为第一个参数
class Dog:
__type='狗'
#类方法,用class来进行修饰
@classmethod
def get_type(cls):
return cls.__type
print(Dog.get_type())
#结果
狗
使用场景
当方法中需要使用类对象(如访问私有类属性),定义类方法
类方法一般如类属性配合使用
注意
类中定义了同名的对象方法,类方法以及静态方法时,调用方法会优先执行最后定义的方法
class Dog:
def demo_method(self):
print('对象方法')
@classmethod
def demo_method(cls):
print('类方法')
@staticmethod
def demo_method(): #最后定义,优先调用
print('静态方法')
dog=Dog()
Dog.demo_method()
dog.demo_method()
#结果
静态方法
静态方法
property
引入
在python中主要为属性提供一个便利的操作方式。
如果我们现在需要设计一个银行账户类,这个类中包含账号人的姓名,余额。
不考虑具体的接口
class Account(object):
def __init__(self,name,money):
self.name=name
self.money=money
存在的问题:不安全(虽然设计方便,但是所有属性可以外部访问修改)
改进1:隐藏细节,使用私有属性__.属性名
对于账号信息而言,金额不允许让用户直接修改。(如果修改,只能去窗口办理)
class Account(object):
def __init__(self,name,money):
self.__name=name
self.__money=money
代码改进后,确实能够增加安全性,让外部无法访问及修改。但是此时,如果是合法操作的访问和修改
(银行窗口),也无法完成,需要添加相应方法去访问。
**改进2:**添加相应的方法
class Account(object):
def __init__(self,name,money):
self.__name=name
self.__money=money
def get_name(self):
return self.__name
def set_balance(self,money):
self.__money=money
def get_balance(self):
return self.__money
改进3:保证数据的有效性
如果self.__money输入不合法(如输入一个字符串abc
)
class Account(object):
def __init__(self,name,money):
self.__name=name
self.__money=money
def get_name(self):
return self.__name
def set_balance(self,money):
if isinstance(money,int):
if money>0 :
self.__money=money
else:
raise ValueError('输入金额不正确')
else:
raise ValueError('输入的数据类型不对')
def get_balance(self):
return self.__money
经过几次更改,程序更加实用,安全性也越来越高,但是复杂度也在增加
实例
在python中,提供一个property类,通过对创建这个类的对象的设置,在使用对象的私有属性时,可以
不再使用属性的函数的调用方式,而是像普通的公有属性一样取使用,方便开发人员使用。
property(fget
=None,fset
=None, fdel
=None, doc=None)
fget
:属性的获取方法fset
: 属性的设置方法fde
l:属性的删除方法- doc:属性描述
property是一个类,__init__
方法由四个参数组成,实例后返回一个用来操作属性的对象
class Account(object):
def __init__(self,name,money):
self.__name=name
self.__money=money
def __get_name(self):
return self.__name
def set_balance(self,money):
if isinstance(money,int):
if money>0 :
self.__money=money
else:
raise ValueError('输入金额不正确')
else:
raise ValueError('输入的数据类型不对')
def get_balance(self):
return self.__money
#设置property类来为属性设置便利的访问方式
name=property(__get_name)
balance=property(get_balance,set_balance)
ac=Account('bob',18000)
print(ac.name)
print(ac.balance)
ac.balance=28000
print(ac.balance)
#结果:
bob
18000
28000
基本形式
class C:
def __init__(self):
self._x = None
def getx(self):
return self._x
def setx(self, value):
self._x = value
def delx(self):
del self._x
x = property(getx, setx, delx, "I'm the 'x' property.")
c=C()
print(c.x)
c.x=1
print(c.x)
del c.x
print(c.x)
#结果
None #未设置值,直接读取初始值为None
Traceback (most recent call last):
1 #完成赋值,读取新的值为1
File "D:/text.py", line 46, in <module> #删除后该值不存在,报错
print(c.x)
File "D:/text.py", line 32, in getx
return self._x
AttributeError: 'C' object has no attribute '_x'
使用装饰器
class C:
def __init__(self):
self._x = None
@property
def x(self):
"""I'm the 'x' property."""
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
c=C()
print(c.x)
c.x=1
print(c.x)
del c.x
print(c.x)
#结果与上面的完全相同
总结
property最大的好处就是在类中把一个方法变成属性调用,起到既能检查属性,还能用属性的方式来访问该属性的作用。进行不同的操作(删改查)我们可以不需要记相应的方法的名称,而是直接对x
进行操作
self
引入
如果对象的方法中需要使用该对象的属性,怎么处理
关键字self
主要用于对象方法中,表示调用该方法的对象。
在方法中使用self,可以获取到调用当前方法的对象,进而获取该对象的属性和方法。
调用对象的方法时,某个对象调用其方法时,python解释器会把这个对象作为第一个参数传递给方法,所有,开发者只需要在定义的时候预留第一个参数self即可。
class Cat:
#方法
def introduce(self):
print('aaa')
cat=Cat() #实例化
cat.introduce() #能够输出
Cat.introduce() #self未捕捉,将其视为普通的函数,直接报错没有传入位置参数
使用self操作属性和对象的变量名在效果上类似。如果属性在赋值时还没有被定义。就会自动定义一个属性并赋值。
补充三种方法
__new__
方法
创建对象时,系统会自动调用__new__
方法。
开发者可以使用__new__
方法自定义对象的创建过程。
- __new__
至少要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供
- __new__
必须要有返回值,返回实例化出来的实例,这点在自己实现__new__
时要特别注意,可以return父类__new__
出来的实例,或者直接是object的__new__
出来的实例
- __init__
有一个参数self,就是这个__new__
返回的实例,__init__
在__new__
的基础上可以完成一些其它初始化的动作,__init__
不需要返回值
- 如果创建对象时传递了自定义参数,且重写了new方法,则new也必须 “预留” 该形参,否则__init__
方法将无法获取到该参数
class Cat:
def __new__(cls, name):
print('创建对象')
return object.__new__(cls)
def __init__(self,name):
print('对象初始化')
self.name=name
def __str__(self):
return '%s' % self.name
cat=Cat('猫')
print(cat)
#结果:
创建对象
对象初始化
猫
实例
class A(object):
def __new__(cls, x):
print ('this is in A.__new__, and x is ', x)
return super(A, cls).__new__(cls)
def __init__(self, y):
print ('this is in A.__init__, and y is ', y)
class B(A):
def __new__(cls, z):
print ('this is in B.__new__, and z is ', z)
return A.__new__(cls, z)
def __init__(self, m):
print ('this is in B.__init__, and m is ', m)
a = A(100)
print ('=' * 20)
b = B(200)
print (type(b))
#结果
this is in A.__new__, and x is 100
this is in A.__init__, and y is 100
====================
this is in B.__new__, and z is 200
this is in A.__new__, and x is 200
this is in B.__init__, and m is 200
<class '__main__.B'>
注释
1.定义A类作为下面类的父类,A类继承object类,因为需要重写A类的__new__()
函数,所以需要继承object基类,成为新式类,经典类没有__new__()
函数;
2.子类在重写__new__()
函数时,写return时必须返回有继承关系的类的__new__()
函数调用,即上面代码中的B类继承自A类,则重写B类的__new__()
函数,写return时,只能返回A.__new__(cls)
或者object.__new__(cls)
,不能返回C类的;
3.由注释掉的代码执行结果可以看出,B类虽然继承自A类,但是如果没有重写B类的__new__()
函数,则默认继承的仍是object基类的__new__(),而不是A的;
4.B类的__new__()函数会在B类实例化时被调用,自动执行其中的代码语句,但是重写__new__()
函数不会影响类的实例化结果,也就是说不管写return时返回的是A的还是object的,B类的实例化对象就是B类的,而不会成为A类的实例化对象;只是在实例化时,如果返回的是A.__new__(cls)
,则会执行A类中定义的__new__()
函数;
5.new()函数确定了类的参数的个数,object类默认定义的__new__()
函数的参数为(cls, *args)
,但如果在子类中重写了__new__(cls, x)
, 则实例化类时,需要传入一个x参数,而__init__()
函数接受到的有两个参数,一个是实例化生成的实例对象self代替,一个是传入的实参x的值;
特性
__new__()
方法是在类准备将自身实例化时调用。__new__
()方法始终都是类的静态方法,即使没有被加上静态方法装饰器。是因为无论怎样重写类的__new__()
函数,追溯到源头都是继承自object的__new__()
函数,而object类中定义的__new__()
函数就被定义成了静态函数,被@stacitmethod
修饰
__call__
方法
对象后面加括号,触发执行
构造方法的执行是由创建对象触发的,即:对象=类名( )
而对于,__call__
方法的执行是由对象后加括号触发的,即对象( )或者类( )
class A(object):
def __call__(self, x):
print ('__call__ called, print x: ', x)
a = A() #与__init__不同,在实例化时不需要传参
a('123') #此时直接给实例对象a传参
__call__ called, print x: 123
看a(‘123’)这是函数的调用方法,这里a实际上是类对象A的实例对象,实例对象能想函数一样传参并被调用,就是__call__()
方法的功能;
__doc__
方法
类的注释,无法继承给子类的
class foo():
'''这是一个类:foo'''
pass
class bar(foo):
pass
print(foo.__doc__)
print(bar.__doc__)
#结果
这是一个类:foo
None
综合上述方法实例
class A(object):
def __init__(self, x):
print('x in __init__', x)
def __new__(cls, y):
print('y in __new__', y)
return super(A, cls).__new__(cls)
def __call__(self, z):
print('z in __call__', z)
A('123')('abc')
#结果
y in __new__ 123 #new中获取传入的参数
x in __init__ 123 #init构造传入的参数形成实例
z in __call__ abc #A('123')现当于一个实例a,a将abc传给call