1、类中可以调用外部函数(函数名),也可以调用类中已经定义的方法(self.方法名)
# 调用外部函数
def sort(self):
"""收到发牌后,对手里的牌进行排序"""
self._hold_cards.sort(key=key1, reverse=True) # 调用一个外部的排序函数key1进行排序
return self._hold_cards
def key1(card):
"""定义排序依据,排序函数以Card类的实例作为参数"""
flower = {'♥': 1, '♠': 2, '♣': 3, '♦': 4} # 花色排序
sort_key = (card.num, flower[card.flower])
return sort_key
# 调用内部函数
def next(self):
"""发牌"""
if self.if_remain(): # 调用内部函数判断牌还有没有
card = self._cards[self._count] # count作为索引,最大等于len(cards)-1
self._count += 1
return card
else:
return None
def if_remain(self):
"""判断牌是否还有,True为还有,False代表发完"""
return self._count < len(self._cards) # 返回一个布尔值
2、定义__str__
或__repr__
方法的重要性(来源:百度AI回复)
如果没有定义__str__或__repr__方法,而后续代码中又用到了它们,会出现不同的行为,这取决于是在什么情况下使用它们。下面我将通过几个例子来说明可能出现的问题:
例子 1:未定义__str__但尝试打印对象
如果你没有为自定义的类定义__str__方法,而尝试使用print函数来打印该类的对象,Python 会调用该对象的__repr__方法(如果定义了的话)。如果__repr__方法也没有定义,那么Python会使用默认的__repr__实现,这通常会输出一个包含对象类型、内存地址等信息的字符串,这对于用户来说可能并不友好。
python
class Card:
def __init__(self, suit, rank):
self.suit = suit
self.rank = rank
# 未定义 __str__ 和 __repr__
card = Card('Hearts', 'A')
print(card) # 输出类似: <__main__.Card object at 0x7f85b9d9a160>
在这个例子中,由于Card类没有定义__str__或__repr__方法,所以print(card)将输出一个默认的对象表示,这通常包含对象类型和内存地址,对用户来说不够直观。
例子 2:未定义__repr__但在交互式环境中直接输入对象
如果你在交互式解释器中直接输入一个对象的名字(而不是使用print函数),Python 会尝试调用该对象的__repr__方法来获取一个可以重新创建对象的表示。如果__repr__方法没有定义,那么同样会使用默认的__repr__实现,输出包含对象类型和内存地址的字符串。
# 假设 Card 类只定义了 __str__ 而没有定义 __repr__
class Card:
def __init__(self, suit, rank):
self.suit = suit
self.rank = rank
def __str__(self):
return f"{self.rank} of {self.suit}"
card = Card('Hearts', 'A')
# 在交互式环境中直接输入对象名
card # 输出类似: <__main__.Card object at 0x7f85b9d9a160>
在这个例子中,尽管我们定义了__str__方法,但在交互式环境中直接输入card时,由于没有定义__repr__方法,Python仍然使用了默认的__repr__实现来显示对象。
例子 3:在需要字符串转换的场景中使用对象如果你尝试在需要字符串的场景中使用对象,比如作为字典的键或者拼接进一个字符串中,但没有定义__str__方法(并且也没有合适的__repr__方法),那么可能会导致错误或者得到不可预测的结果。
# 假设 Card 类既没有 __str__ 也没有 __repr__
class Card:
def __init__(self, suit, rank):
self.suit = suit
self.rank = rank
card = Card('Hearts', 'A')
# 尝试将对象作为字典的键
deck = {card: 'Ace of Hearts'} # 这通常会工作,但键的显示会不友好
print(deck) # 输出可能包含对象的默认表示,如 {<__main__.Card object at 0x...>: 'Ace of Hearts'}
# 尝试将对象拼接进字符串
message = "You have the " + card # 这会抛出 TypeError,因为对象不能直接转换为字符串
在最后一个例子中,尝试将Card对象作为字典的键时,尽管技术上可以工作,但字典的显示将包含对象的默认表示,这通常不是我们所期望的。而尝试将对象直接拼接进字符串时,由于对象不是字符串类型,会抛出TypeError异常。
综上所述,未定义__str__或__repr__方法可能会导致输出不够友好、调试困难以及在需要字符串转换的场景中出现问题。为了提供清晰的用户界面和方便的调试体验,通常建议为自定义的类实现这两个方法。
3、需要每个类都定义__str__和__repr__方法吗?一般哪些类需要
在Python中,并不是每个类都需要定义__str__
和__repr__
方法。这两个方法的主要区别在于它们的用途和预期的输出:
__str__
:这个方法返回一个“友好的”字符串表示,通常是给人阅读的。当你尝试打印一个对象或者将对象转换为字符串时(比如使用print(obj)
或str(obj)
),Python会调用这个对象的__str__
方法。__repr__
:这个方法返回一个对象的“官方”字符串表示,通常是给解释器或开发者阅读的。它应该是一个有效的Python表达式,能够用来重新创建该对象。当你尝试在解释器中直接输入对象名或使用repr(obj)
时,Python会调用这个对象的__repr__
方法。
通常情况下,以下类型的类可能需要定义这两个方法:
-
自定义数据类型:当你创建自定义的数据类型时,比如一个表示复杂数据结构或业务逻辑的类,定义
__str__
和__repr__
方法可以帮助你更好地控制和展示对象的状态。 -
需要调试的类:在开发过程中,能够清楚地看到对象的内部状态是非常有用的。通过提供有意义的
__repr__
输出,你可以更容易地调试和理解代码。 -
用于交互的类:如果你的类将被用作命令行工具、Web框架或其他交互式环境中的一部分,那么提供清晰、有用的
__str__
输出可能会提高用户体验。 -
不可变类型:对于不可变类型(如元组或冻结集),
__repr__
方法通常需要返回一个可以重新创建该对象的表达式。 -
需要序列化的类:当对象需要被序列化为字符串(比如JSON或XML)时,定义
__str__
或__repr__
可以帮助控制序列化的格式。
在实际应用中,__str__
方法通常比__repr__
方法更常用,因为它提供了更友好的输出。然而,__repr__
方法也是非常重要的,特别是在需要调试或复制对象时。
如果你不确定是否需要定义这两个方法,可以先尝试不定义它们,看看默认的输出是否满足你的需求。如果默认的输出不够清晰或有用,那么再考虑添加__str__
和/或__repr__
方法。
4、属性如果定义了@property和setter方法,受不受保护一样了,都可以访问和修改。所以,不推荐一边保护一边设置无限制的@property和setter方法,除了增加代码长度毫无意义
当需要访问和修改时,可以不设置保护;
或者不设置@property让其完全私有,不许修改和访问;
或者使用内部方法(如定义一个修改的方法)而不是setter修改,来控制哪些操作是允许的
5、isinstance(object.class)检查一个对象是否属于一个类或者他的子类,instance,实例。如果对象是该类型的实例或该类型的子类的实例,则返回True,否则返回False。如:
def main():
emps = [
Manager('刘备'), Programmer('诸葛亮'),
Manager('曹操'), Salesman('荀彧'),
Salesman('吕布'), Programmer('张辽'),
Programmer('赵云')
]
for emp in emps:
if isinstance(emp, Programmer):
emp.working_hour = int(input('请输入%s本月工作时间: ' % emp.name))
elif isinstance(emp, Salesman):
emp.sales = float(input('请输入%s本月销售额: ' % emp.name))
# 同样是接收get_salary这个消息但是不同的员工表现出了不同的行为(多态)
print('%s本月工资为: ¥%s元' %
(emp.name, emp.get_salary()))