Python的hasattr() getattr() setattr() 函数
# hasattr(object, name)
# 判断一个对象里面是否有name属性或者name方法,返回BOOL值,有name特性返回True, 否则返回False。
class test1():
def __init__(self):
self.name='xiaohua'
def run_test(self):
print('hello {}'.format(self.name))
test=test1()
test.run_test() # hello xiaohua
print(vars(test)) # {'name': 'xiaohua'}
print(vars(test1)) # {'__module__': '__main__', '__doc__': None, '__dict__': <attribute '__dict__' of 'test1' objects>, '__weakref__': <attribute '__weakref__' of 'test1' objects>, '__init__': <function test1.__init__ at 0x0000000002D8CC80>, 'run_test': <function test1.run_test at 0x0000000002D8CD08>}
print(hasattr(test,'name')) # True
print(hasattr(test1, 'run_test')) # True,但是如果换成了name 就是false
# setattr(object, name, values)
# 给对象的属性赋值,若属性不存在,先创建再赋值。
setattr(test, "age", "18") #为属相赋值,并没有返回值
print(hasattr(test, "age"))
print(getattr(test,'age'))
# True
# 18
Python属性的get/set 方法
class Critter(object):
'''A virtual pet'''
def __init__(self, name):
print ('A new critter has been born!')
self.__name = name
def get_name(self):
return self.__name
def set_name(self, new_name):
if new_name == '':
print ('A critter name can not be the empty string.')
else:
self.__name = new_name
print ('Name change successful.')
name = property(get_name, set_name)
def talk(self):
print('Hi, I am %s' % self.name)
crit = Critter('Poochie')
# A new critter has been born!
crit.talk() # Hi, I am Poochie
print(crit.name) #Poochie
python3 中的format
在Python 3.0中,%操作符通过一个更强的格式化方法format()进行了增强,这个用来格式化的模版使用大括号({,})作为特殊字符
print('User ID: {0}'.format('root')) # positional argument
# Use the named keyword arguments
print('User ID: {uid} Last seen: {last_login}'.format(
uid='root',
last_login = '5 Mar 2008 07:20'))
# outputs:
# User ID: root
# User ID: root Last seen: 5 Mar 2008 07:20
eval()函数:将字符串str当成有效的表达式来求值并返回计算结果
语法: eval(source[, globals[, locals]]) -> value
参数:
source:一个Python表达式或函数compile()返回的代码对象
globals:可选。必须是dictionary
locals:可选。任意map对象
可以把list,tuple,dict和string相互转化。
# 字符串转换成列表
a = "[[1,2], [3,4], [5,6], [7,8], [9,0]]"
print(isinstance(a,str)) # True
print(type(a)) # <class 'str'>
b = eval(a)
print(b) # [[1, 2], [3, 4], [5, 6], [7, 8], [9, 0]]
print(type(b)) # <class 'list'>
# 字符串转换成字典
a1 = "{1: 'a', 2: 'b'}"
b1 = eval(a1)
print(b1) # {1: 'a', 2: 'b'}
print(type(b1)) # <class 'dict'>
# 字符串转换成元组
a2 = "([1,2], [3,4], [5,6], [7,8], (9,0))"
b2 = eval(a2)
print(b2) # ([1, 2], [3, 4], [5, 6], [7, 8], (9, 0))
print(type(b2)) # <class 'tuple'>
从我的理解就是将其转化成双引号内最外层的字符
改变对象的字符串显示
repr函数,对应repr(object)这个函数,返回一个可以用来表示对象的可打印字符串.
class Pair:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return 'Pair({0.x!r}, {0.y!r})'.format(self)
def __str__(self):
return '({0.x!s}, {0.y!s})'.format(self)
pair=Pair(3,5)
print(pair) # 调用__str__, 因为使用了print
print(Pair(3,5)) # 调用__str__
# >>> p = Pair(3, 4)
# >>> p
# Pair(3, 4) # __repr__() output
# __repr__() 方法返回一个实例的代码表示形式,通常用来重新构造这个实例。
# 内置的 repr() 函数返回这个字符串,跟我们使用交互式解释器显示的值是一样的。
# __str__() 方法将实例转换为一个字符串,使用 str() 或 print() 函数会输出这个字符串。
# 我们在这里还演示了在格式化的时候怎样使用不同的字符串表现形式。
# 特别来讲,!r 格式化代码指明输出使用 __repr__() 来代替默认的 __str__() 。
print('p is {0!r}'.format(pair)) # p is Pair(3, 5)
print('p is {0}'.format(pair)) # p is (3, 5)
# 上面的 format() 方法的使用看上去很有趣,格式化代码 {0.x} 对应的是第1个参数的x属性。
# 因此,在下面的函数中,0实际上指的就是 self 本身
# 作为这种实现的一个替代,你也可以使用 % 操作符,就像下面这样
# def __repr__(self):
# return 'Pair(%r, %r)' % (self.x, self.y)
自定义字符串的格式化,自定义format函数
你想通过 format() 函数和字符串方法使得一个对象能支持自定义的格式化
_formats = {
'ymd' : '{d.year}-{d.month}-{d.day}',
'mdy' : '{d.month}/{d.day}/{d.year}',
'dmy' : '{d.day}/{d.month}/{d.year}'
}
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
def __format__(self, code):
if code == '':
code = 'ymd'
fmt = _formats[code]
return fmt.format(d=self) # '{d.year}-{d.month}-{d.day}'.format(d)
d = Date(2012, 12, 21)
print(d) # <__main__.Date object at 0x0000000002D00048>
print(format(d)) # 2012-12-21
print(format(d,'mdy')) # 12/21/2012
# 直接用于date模块
from datetime import date
d = date(2012, 12, 21)
print(format(d,'%A, %B %d, %Y'))
print('The end is {:%d %b %Y}. Goodbye'.format(d))
了让一个对象兼容 with 语句,你需要实现 enter() 和 exit() 方法
from socket import socket, AF_INET, SOCK_STREAM
class LazyConnection:
def __init__(self, address, family=AF_INET, type=SOCK_STREAM):
self.address = address
self.family = family
self.type = type
self.sock = None
def __enter__(self):
if self.sock is not None:
raise RuntimeError('Already connected')
self.sock = socket(self.family, self.type)
self.sock.connect(self.address)
return self.sock
def __exit__(self, exc_ty, exc_val, tb):
self.sock.close()
self.sock = None
# 这个类的关键特点在于它表示了一个网络连接,但是初始化的时候并不会做任何事情(比如它并没有建立一个连接)。
# 连接的建立和关闭是使用 with 语句自动完成的
from functools import partial
conn = LazyConnection(('www.python.org', 80))
# Connection closed
with conn as s:
# conn.__enter__() executes: connection open
s.send(b'GET /index.html HTTP/1.0\r\n')
s.send(b'Host: www.python.org\r\n')
s.send(b'\r\n')
resp = b''.join(iter(partial(s.recv, 8192), b''))
# conn.__exit__() executes: connection closed
# 当出现 with 语句的时候,对象的 __enter__() 方法被触发, 它返回的值(如果有的话)会被赋值给 as 声明的变量。
# 然后,with 语句块里面的代码开始执行。 最后,__exit__() 方法被触发进行清理工作。
# 不管 with 代码块中发生什么,上面的控制流都会执行完,就算代码块中发生了异常也是一样的。
# 事实上,__exit__() 方法的第三个参数包含了异常类型、异常值和追溯信息(如果有的话)。
# __exit__() 方法能自己决定怎样利用这个异常信息,或者忽略它并返回一个None值。
# 如果 __exit__() 返回 True ,那么异常会被清空,就好像什么都没发生一样, with 语句后面的程序继续在正常执行。
在类中封装属性名
# Python程序员不去依赖语言特性去封装数据,而是通过遵循一定的属性和方法命名规约来达到这个效果。
# 第一个约定是任何以单下划线_开头的名字都应该是内部实现。
class A:
def __init__(self):
self._internal = 0 # An internal attribute
self.publicP = 1 # A public attribute
def public_method(self):
'''
A public method
'''
pass
def _internal_method(self):
pass
aObj=A()
print(aObj.publicP) # 1
print(aObj.public_method()) # None
# 提示中看不到_开头的函数和属性
# 使用下划线开头的约定同样适用于模块名和模块级别函数
class B:
def __init__(self):
self.__private = 0
def __private_method(self):
pass
def public_method(self):
pass
self.__private_method()
# dir 列出指定对象或类的的所有可用方法
print(dir(B))
# ['_B__private_method', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
# '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__',
# '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
# '__str__', '__subclasshook__', '__weakref__', 'public_method']
print(B._B__private) # Error 可以看出上面的可用方法中没有_B__private这个变量
print(dir(bobj))
# ['_B__private', '_B__private_method', '__class__', '__delattr__', '__dict__',
# '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__',
# '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__',
# '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__',
# 'public_method']
print(bobj._B__private) # 0
# 使用双下划线开始会导致访问名称变成其他形式。
# 比如,在前面的类B中,私有属性会被分别重命名为 _B__private 和 _B__private_method 。
# 这时候你可能会问这样重命名的目的是什么,答案就是继承——这种属性通过继承是无法被覆盖的
class C(B):
def __init__(self):
super().__init__()
self.__private = 1 # Does not override B.__private
# Does not override B.__private_method()
def __private_method(self):
pass
# __private 和 __private_method 被重命名为 _C__private 和 _C__private_method
print(vars(C))
print(vars(B))
# {'__module__': '__main__', '__doc__': None, '_C__private_method': <function C.__private_method at 0x0000000002ED9F28>, '__init__': <function C.__init__ at 0x0000000002ED9EA0>}
# {'_B__private_method': <function B.__private_method at 0x0000000002ED9D90>, '__dict__': <attribute '__dict__' of 'B' objects>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'B' objects>, '__doc__': None, 'public_method': <function B.public_method at 0x0000000002ED9E18>, '__init__': <function B.__init__ at 0x0000000002ED9D08>}
那么dir和vars的区别是什么:
dir():默认打印当前模块的所有属性,如果传一个对象参数则打印当前对象的属性
vars():默认打印当前模块的所有属性,如果传一个对象参数则打印当前对象的属性
dir()和vars()的区别就是dir()只打印属性(属性,属性……)而vars()则打印属性与属性的值(属性:属性值……)
创建可管理的属性,给某个实例attribute增加除访问与修改之外的其他处理逻辑,比如类型检查或合法性验证
自定义某个属性的一种简单方法是将它定义为一个property
class Person:
def __init__(self, first_name):
self.first_name = first_name
# Getter function
@property
def first_name(self): # 必须和属性名一样
# return self._first_name # 此方法对应的是setter方法中定义的_first_name和上面的是两码事
return self.name # 将其换成了name也okay只要setter中定义了
# Setter function
@first_name.setter
def first_name(self, value):
if not isinstance(value, str):
raise TypeError('Expected a string')
# self._first_name = value
self.name=value
# Deleter function (optional)
@first_name.deleter
def first_name(self):
raise AttributeError("Can't delete attribute")
# 这三个方法的名字都必须一样,和属性名一样
# 第一个方法是一个 getter 函数,它使得 first_name 成为一个属性。
# 其他两个方法给 first_name 属性添加了 setter 和 deleter 函数。
# 需要强调的是只有在 first_name 属性被创建后,
# 后面的两个装饰器 @first_name.setter 和 @first_name.deleter 才能被定义。
# property的一个关键特征是它看上去跟普通的attribute没什么两样, 但是访问它的时候会自动触发 getter 、setter 和 deleter 方法
# p=Person(100)
# print(p.first_name) TypeError: Expected a string
p=Person('Young')
print(p.first_name) # Young
p.first_name = 'Alvin'
print(p.first_name) # Alvin
# p.first_name = 42 TypeError: Expected a string
# 在实现一个property的时候,底层数据(如果有的话)仍然需要存储在某个地方。
# 因此,在get和set方法中,你会看到对 _first_name 属性的操作,这也是实际数据保存的地方。
# 另外,你可能还会问为什么 __init__() 方法中设置了 self.first_name 而不是 self._first_name 。
# 在这个例子中,我们创建一个property的目的就是在设置attribute的时候进行检查。
# 因此,你可能想在初始化的时候也进行这种类型检查。通过设置 self.first_name ,自动调用 setter 方法, 这个方法里面会进行参数的检查,
# 否则就是直接访问 self._first_name 了。
注意是先setter后getter
还能在已存在的get和set方法基础上定义property, 同上
class Person:
def __init__(self, first_name):
self.set_first_name(first_name)
# Getter function
def get_first_name(self):
return self._first_name
# Setter function
def set_first_name(self, value):
if not isinstance(value, str):
raise TypeError('Expected a string')
self._first_name = value
# Deleter function (optional)
def del_first_name(self):
raise AttributeError("Can't delete attribute")
# Make a property from existing get/set methods
name = property(get_first_name, set_first_name, del_first_name)
# 一个property属性其实就是一系列相关绑定方法的集合。
# 如果你去查看拥有property的类, 就会发现property本身的fget、fset和fdel属性就是类里面的普通方法。
# 通常来讲,你不会直接取调用fget或者fset,它们会在访问property的时候自动被触发
print(dir(Person))
print(Person.first_name.fget)
print(Person.first_name.getter)
# <function Person.first_name at 0x0000000002D5D0D0>
# <built-in method getter of property object at 0x0000000002D4FAE8>
print(Person.__dict__)
print(vars(Person))
# 二者一样
# {'first_name': <property object at 0x0000000002E7FAE8>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__init__': <function Person.__init__ at 0x0000000002E8D048>, '__doc__': None, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__module__': '__main__'}
# {'first_name': <property object at 0x0000000002E7FAE8>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__init__': <function Person.__init__ at 0x0000000002E8D048>, '__doc__': None, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__module__': '__main__'}
Properties还是一种定义动态计算attribute的方法。 这种类型的attributes并不会被实际的存储,而是在需要的时候计算出来。
import math
class Circle:
def __init__(self, radius):
self.radius = radius
@property
def area(self):
return math.pi * self.radius ** 2
@property
def diameter(self):
return self.radius ** 2
@property
def perimeter(self):
return 2 * math.pi * self.radius
c = Circle(4.0)
print(c.radius)
print(c.area)
print(c.diameter)
print(c.perimeter)
Supper
调用父类方法 super() 函数
super([type [,object-or-type]])
返回一个代理对象, 这个对象负责将方法调用分配给第一个参数的一个父类或者同辈的类去完成.
super()和getattr() 都使用mro属性来解析搜索顺序, mro实际上是一个只读的元组.
如果提供了第二个参数, 则找到的父类对象的self就绑定到这个参数上, 后面调用这个对象的方法时, 可以自动地隐式传递self.
如果第二个参数是一个对象, 则isinstance(obj, type)必须为True. 如果第二个参数为一个类型, 则issubclass(type2, type)必须为True
不带参数的super()只能用在类定义中(因为依赖于caller的第二个参数), 编译器会自动根据当前定义的类填充参数.
也就是说, 后面所有调用super返回对象的方法时, 第一个参数self都是super()的第二个参数.
因为Python中所谓的方法, 就是一个第一个参数为self的函数, 一般在调用方法的时候a.b()会隐式的将a赋给b()的第一个参数.
class A:
def spam(self):
print('A.spam')
class B(A):
def spam(self):
print('B.spam')
super().spam() # Call parent spam()
b=B()
print(b.spam())
# outputs:
# B.spam
# A.spam
# None
super() 函数的一个常见用法是在 init() 方法中确保父类被正确的初始化了
class A:
def __init__(self):
self.x = 0
class B(A):
def __init__(self):
super().__init__() # 如果注释掉这句,AttributeError: 'B' object has no attribute 'x'
self.y = 1
def output(self):
print(self.x, self,y)
b=B()
print(b.x)
print(b.y)
# outputs:
# 0
# 1
# >>> C.__mro__
# (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>,
# <class 'object'>)
# >>>
子类会先于父类被检查
多个父类会根据它们在列表中的顺序被检查
如果对下一个类存在两个合法的选择,选择第一个父类
class d1:
def __init__(self):
print('In d1...')
class d2:
def __init__(self):
print('In d2...')
class d3(d1,d2):
def __init__(self):
print('In d3...')
print(d1.__mro__)
print(d2.__mro__)
# (<class '__main__.d1'>, <class 'object'>)
# (<class '__main__.d2'>, <class 'object'>)
print(d3.mro()) # [<class '__main__.d3'>, <class '__main__.d1'>, <class '__main__.d2'>, <class 'object'>]
print(d3.__mro__) # (<class '__main__.d3'>, <class '__main__.d1'>, <class '__main__.d2'>, <class 'object'>)
#一样
在子类中,扩展定义在父类中的property的功能
class Person:
def __init__(self, name):
self.name = name
# Getter function
@property
def name(self):
return self._name
# Setter function
@name.setter
def name(self, value):
if not isinstance(value, str):
raise TypeError('Expected a string')
self._name = value
# Deleter function
@name.deleter
def name(self):
raise AttributeError("Can't delete attribute")
class SubPerson(Person):
@property
def name(self):
print('Getting name')
return super().name
@name.setter
def name(self, value): #首先调用setter,其次调用getter
print('Setting name to', value)
super(SubPerson, SubPerson).name.__set__(self, value)
@name.deleter
def name(self):
print('Deleting name')
super(SubPerson, SubPerson).name.__delete__(self)
s = SubPerson('Guido') # Setting name to Guido
print(s.name) # 调用了name属性
# Getting name
# Guido
s.name = 'Larry' # Setting name to Larry
s.name = 42 # TypeError: Expected a string
如果你仅仅只想扩展property的某一个方法,那么可以像下面这样写:
class SubPerson(Person):
@Person.name.getter
def name(self):
print('Getting name')
return super().name
或者,你只想修改setter方法,就这么写:
class SubPerson(Person):
@Person.name.setter
def name(self, value):
print('Setting name to', value)
super(SubPerson, SubPerson).name.__set__(self, value)