热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

pythoninit方法_[译]Python学习——__init__()方法4

注:原书作者StevenF.Lott,原书名为MasteringObject-orientedPython没有__init__()的无状态对象下面这

注:原书作者 Steven F. Lott,原书名为 Mastering Object-oriented Python

没有__init__()的无状态对象

下面这个示例,是一个简化去掉了__init__()的类。这是一个常见的Strategy设计模式对象。策略对象插入到主对象来实现一种算法或者决策。它可能依赖主对象的数据,策略对象自身可能没有任何数据。我们经常设计策略类来遵循Flyweight设计模式:我们避免在Strategy对象内部进行存储。所有提供给Strategy的值都是作为方法的参数值。Strategy对象本身可以是无状态的。这更多是为了方法函数的集合而非其他。

在本例中,我们为Player实例提供了游戏策略。下面是一个抓牌和减少其他赌注的策略示例(比较笨的策略):

class GameStrategy:

def insurance(self, hand):

return False

def split(self, hand):

return False

def double(self, hand):

return False

def hit(self, hand):

return sum(c.hard for c in hand.cards) <&#61; 17

每个方法都需要当前的Hand作为参数值。决策是基于可用信息的&#xff0c;也就是指庄家的牌和闲家的牌。

我们可以使用不同的Player实例来构建单个策略实例&#xff0c;如下面代码片段所示&#xff1a;

dumb &#61; GameStrategy()

我们可以想象创造一组相关的策略类&#xff0c;在21点中玩家可以针对各种决策使用不同的规则。

一些额外的类定义

如前所述&#xff0c;一个玩家有两个策略&#xff1a;一个用于下注&#xff0c;一个用于出牌。每个Player实例都与模拟计算执行器有一序列的交互。我们称计算执行器为Table类。

Table类需要Player实例提供以下事件&#xff1a;

玩家必须基于下注策略来设置初始赌注。

玩家将得到一手牌。

如果手牌是可分离的&#xff0c;玩家必须决定是分离或不基于出牌策略。这可以创建额外的Hand实例。在一些赌场&#xff0c;额外的一手牌也是可分离的。

对于每个Hand实例&#xff0c;玩家必须基于出牌策略来决定是要牌、加倍或停牌。

玩家会获得奖金&#xff0c;然后基于输赢情况调整下注策略。

从这&#xff0c;我们可以看到Table类有许多API方法来获得赌注&#xff0c;创建Hand对象提供分裂、分解每一手牌、付清赌注。这个对象跟踪了一组Players的出牌状态。

以下是处理赌注和牌的Table类&#xff1a;

class Table:

def __init__(self):

self.deck &#61; Deck()

def place_bet(self, amount):

print("Bet", amount)

def get_hand(self):

try:

self.hand &#61; Hand2(d.pop(), d.pop(), d.pop())

self.hole_card &#61; d.pop()

except IndexError:

# Out of cards: need to shuffle.

self.deck &#61; Deck()

return self.get_hand()

print("Deal", self.hand)

return self.hand

def can_insure(self, hand):

return hand.dealer_card.insure

Player使用Table类来接收赌注&#xff0c;创建一个Hand对象&#xff0c;出牌时根据这手牌来决定是否买保险。使用额外方法去获取牌并决定偿还。

在get_hand()中展示的异常处理不是一个精确的赌场玩牌模型。这可能会导致微小的统计误差。更精确的模拟需要编写一副牌&#xff0c;当空的时候可以重新洗牌&#xff0c;而不是抛出异常。

为了正确地交互和模拟现实出牌&#xff0c;Player类需要一个下注策略。下注策略是一个有状态的对象&#xff0c;决定了初始赌注。各种下注策略调整赌注通常都是基于游戏的输赢。

理想情况下&#xff0c;我们渴望有一组下注策略对象。Python的装饰器模块允许我们创建一个抽象超类。一个非正式的方法创建策略对象引发的异常必须由子类实现。

我们定义了一个抽象超类&#xff0c;此外还有一个具体子类定义了固定下注策略&#xff0c;如下所示&#xff1a;

class BettingStrategy:

def bet(self):

raise NotImplementedError("No bet method")

def record_win(self):

pass

def record_loss(self):

pass

class Flat(BettingStrategy):

def bet(self):

return 1

超类定义了带有默认值的方法。抽象超类中的基本bet()方法抛出异常。子类必须覆盖bet()方法。其他方法可以提供默认值。这里给上一节的游戏策略添加了下注策略&#xff0c;我们可以看看Player类周围更复杂的__init__()方法。

我们可以利用abc模块正式化抽象超类的定义。就像下面的代码片段那样&#xff1a;

import abc

class BettingStrategy2(metaclass&#61;abc.ABCMeta):

&#64;abstractmethod

def bet(self):

return 1

def record_win(self):

pass

def record_loss(self):

&#xfffc; pass

这样做的优势在于创建了BettingStrategy2的实例&#xff0c;不会造成任何子类bet()的失败。如果我们试图通过未实现的抽象方法来创建这个类的实例&#xff0c;它将引发一个异常来替代创建对象。

是的&#xff0c;抽象方法有一个实现。它可以通过super().bet()来访问。

多策略的__init__()

我们可从各种来源创建对象。例如&#xff0c;我们可能需要复制一个对象作为创建备份或冻结一个对象的一部分&#xff0c;以便它可以作为字典的键或被置入集合中&#xff1b;这是内置类set和frozenset背后的想法。

有几个总体设计模式&#xff0c;它们有多种方法来构建一个对象。一个设计模式就是一个复杂的__init__()&#xff0c;称为多策略初始化。同时&#xff0c;有多个类级别的(静态)构造函数的方法。

这些都是不兼容的方法。他们有完全不同的接口。

避免克隆方法

在Python中&#xff0c;一个克隆方法没必要复制一个不需要的对象。使用克隆技术表明可能是未能理解Python中的面向对象设计原则。

克隆方法封装了在错误的地方创建对象的常识。被克隆的源对象不能了解通过克隆建立的目标对象的结构。然而&#xff0c;如果源对象提供了一个合理的、得到了良好封装的接口&#xff0c;反向(目标对象有源对象相关的内容)是可以接受的。

我们这里展示的例子是有效的克隆&#xff0c;因为它们很简单。我们将在下一章展开它们。然而&#xff0c;展示这些基本技术是用来做更多的事情&#xff0c;而不是琐碎的克隆&#xff0c;我们看看将可变对象Hand冻结为不可变对象。

下面可以通过两种方式创建Hand对象的示例&#xff1a;

class Hand3:

def __init__(self, *args, **kw):

if len(args) &#61;&#61; 1 and isinstance(args[0], Hand3):

# Clone an existing hand; often a bad idea

other &#61; args[0]

self.dealer_card &#61; other.dealer_card

self.cards &#61; other.cards

else:

# Build a fresh, new hand.

dealer_card, *cards &#61; args

self.dealer_card &#61; dealer_card

self.cards &#61; list(cards)

第一种情况&#xff0c;从现有的Hand3对象创建Hand3实例。第二种情况&#xff0c;从单独的Card实例创建Hand3对象。

与frozenset对象的相似之处在于可由单独的项目或现有set对象创建。我们将在下一章学习创建不可变对象。使用像下面代码片段这样的构造&#xff0c;从现有的Hand创建一个新的Hand使得我们可以创建一个Hand对象的备份&#xff1a;

h &#61; Hand(deck.pop(), deck.pop(), deck.pop())

memento &#61; Hand(h)

我们保存Hand对象到memento变量中。这可以用来比较最后处理的牌与原来手牌&#xff0c;或者我们可以在集合或映射中使用时冻结它。

1. 更复杂的初始化选择

为了编写一个多策略初始化&#xff0c;我们经常被迫放弃特定的命名参数。这种设计的优点是灵活&#xff0c;但缺点是不透明的、毫无意义的参数命名。它需要大量的用例文档来解释变形。

我们还可以扩大我们的初始化来分裂Hand对象。分裂Hand对象的结果是只是另一个构造函数。下面的代码片段说明了如何分裂Hand对象&#xff1a;

class Hand4:

def __init__(self, *args, **kw):

if len(args) &#61;&#61; 1 and isinstance(args[0], Hand4):

# Clone an existing handl often a bad idea

other &#61; args[0]

self.dealer_card &#61; other.dealer_card

self.cards&#61; other.cards

elif len(args) &#61;&#61; 2 and isinstance(args[0], Hand4) and &#39;split&#39; in kw:

# Split an existing hand

other, card &#61; args

self.dealer_card &#61; other.dealer_card

self.cards &#61; [other.cards[kw[&#39;split&#39;]], card]

elif len(args) &#61;&#61; 3:

# Build a fresh, new hand.

dealer_card, *cards &#61; args

self.dealer_card &#61; dealer_card

self.cards &#61; list(cards)

else:

raise TypeError("Invalid constructor args&#61;{0!r} kw&#61;{1!r}".format(args, kw))

def __str__(self):

return ", ".join(map(str, self.cards))

这个设计包括获得额外的牌来建立合适的、分裂的手牌。当我们从一个Hand4对象创建一个Hand4对象&#xff0c;我们提供一个分裂的关键字参数&#xff0c;它从原Hand4对象使用Card类索引。

下面的代码片段展示了我们如何使用被分裂的手牌&#xff1a;

d &#61; Deck()

h &#61; Hand4(d.pop(), d.pop(), d.pop())

s1 &#61; Hand4(h, d.pop(), split&#61;0)

s2 &#61; Hand4(h, d.pop(), split&#61;1)

我们创建了一个Hand4初始化的h实例并分裂到两个其他Hand4实例&#xff0c;s1和s2&#xff0c;并处理额外的Card类。21点的规则只允许最初的手牌有两个牌值相等。

虽然这个__init__()方法相当复杂&#xff0c;它的优点是可以并行的方式从现有集创建fronzenset。缺点是它需要一个大文档字符串来解释这些变化。

2. 初始化静态方法

当我们有多种方法来创建一个对象时&#xff0c;有时会更清晰的使用静态方法来创建并返回实例&#xff0c;而不是复杂的__init__()方法。

也可以使用类方法作为替代初始化&#xff0c;但是有一个实实在在的优势在于接收类作为参数的方法。在冻结或分裂Hand对象的情况下&#xff0c;我们可能需要创建两个新的静态方法冻结或分离对象。使用静态方法作为代理构造函数是一个小小的语法变化&#xff0c;但当组织代码的时候它拥有巨大的优势。

下面是一个有静态方法的Hand&#xff0c;可用于从现有的Hand实例构建新的Hand实例&#xff1a;

class Hand5:

def __init__(self, dealer_card, *cards):

self.dealer_card &#61; dealer_card

self.cards &#61; list(cards)

&#64;staticmethod

def freeze(other):

hand &#61; Hand5(other.dealer_card, *other.cards)

return hand

&#64;staticmethod

def split(other, card0, card1 ):

hand0 &#61; Hand5(other.dealer_card, other.cards[0], card0)

hand1 &#61; Hand5(other.dealer_card, other.cards[1], card1)

return hand0, hand1

def __str__(self):

return ", ".join(map(str, self.cards))

一个方法冻结或创建一个备份。另一个方法分裂Hand5实例来创建两个Hand5实例。

这更具可读性并保存参数名的使用来解释接口。

下面的代码片段展示了我们如何通过这个版本分裂Hand5实例&#xff1a;

d &#61; Deck()

h &#61; Hand5(d.pop(), d.pop(), d.pop())

s1, s2 &#61; Hand5.split(h, d.pop(), d.pop())

我们创建了一个初始的Hand5的h实例&#xff0c;分裂成两个手牌&#xff0c;s1和s2&#xff0c;处理每一个额外的Card类。split()静态方法比__init__()简单得多。然而&#xff0c;它不遵循从现有的set对象创建fronzenset对象的模式。

更多的__init__()技巧

我们会看看一些其他更高级的__init__()技巧。在前面的部分这些不是那么普遍有用的技术。

下面是Player类的定义&#xff0c;使用了两个策略对象和table对象。这展示了一个看起来并不舒服的__init__()方法&#xff1a;

class Player:

def __init__(self, table, bet_strategy, game_strategy):

self.bet_strategy &#61; bet_strategy

self.game_strategy &#61; game_strategy

self.table &#61; table

def game(self):

self.table.place_bet(self.bet_strategy.bet())

self.hand &#61; self.table.get_hand()

if self.table.can_insure(self.hand):

if self.game_strategy.insurance(self.hand):

self.table.insure(self.bet_strategy.bet())

# Yet more... Elided for now

Player的__init__()方法似乎只是统计。只是简单传递命名好的参数到相同命名的实例变量。如果我们有大量的参数&#xff0c;简单地传递参数到内部变量会产生过多看似冗余的代码。

我们可以如下使用Player类(和相关对象)&#xff1a;

table &#61; Table()

flat_bet &#61; Flat()

dumb &#61; GameStrategy()

p &#61; Player(table, flat_bet, dumb)

p.game()

我们可以通过简单的传递关键字参数值到内部实例变量来提供一个非常短的和非常灵活的初始化。

下面是使用关键字参数值构建Player类的示例&#xff1a;

class Player2:

def __init__(self, **kw):

"""Must provide table, bet_strategy, game_strategy."""

self.__dict__.update(kw)

def game(self):

self.table.place_bet(self.bet_strategy.bet())

self.hand&#61; self.table.get_hand()

if self.table.can_insure(self.hand):

if self.game_strategy.insurance(self.hand):

self.table.insure(self.bet_strategy.bet())

# etc.

为了简洁而牺牲了大量可读性。它跨越到一个潜在的默默无闻的领域。

因为__init__()方法减少到一行&#xff0c;它消除了某种程度上“累赘”的方法。这个累赘&#xff0c;无论如何&#xff0c;是被传递到每个单独的对象构造函数表达式中。我们必须将关键字添加到对象初始化表达式中&#xff0c;因为我们不再使用位置参数&#xff0c;如下面代码片段所示&#xff1a;

p2 &#61; Player2(table&#61;table, bet_strategy&#61;flat_bet, game_strategy&#61;dumb)

为什么这样做呢&#xff1f;

它有一个潜在的优势。这样的类定义是相当易于扩展的。我们可能只有几个特定的担忧&#xff0c;提供额外关键字参数给构造函数。

下面是预期的用例&#xff1a;

>>> p1 &#61; Player2(table&#61;table, bet_strategy&#61;flat_bet, game_strategy&#61;dumb)

>>> p1.game()

下面是一个额外的用例&#xff1a;

>>> p2 &#61; Player2(table&#61;table, bet_strategy&#61;flat_bet, game_strategy&#61;dumb, log_name&#61;"Flat/Dumb")

>>> p2.game()

我们添加了一个与类定义无关的log_name属性。也许&#xff0c;这可以被用作统计分析的一部分。Player2.log_name属性可以用来注释日志或其他数据的收集。

我们能添加的东西是有限的&#xff1b;我们只能添加没有与内部使用的命名相冲突的参数。类实现的常识是需要的&#xff0c;用于创建没有滥用已在使用的关键字的子类。由于**kw参数提供了很少的信息&#xff0c;我们需要仔细阅读。在大多数情况下&#xff0c;比起检查实现细节我们宁愿相信类是正常工作的。

在超类的定义中是可以做到基于关键字的初始化的&#xff0c;对于使用超类来实现子类会变得稍微的简单些。我们可以避免编写一个额外的__init__()方法到每个子类&#xff0c;当子类的唯一特性包括了简单新实例变量。

这样做的缺点是&#xff0c;我们已经模糊了没有正式通过子类定义记录的实例变量。如果只是一个小变量&#xff0c;整个子类可能有太多的编程开销用于给一个类添加单个变量。然而&#xff0c;一个小变量常常会导致第二个、第三个。不久&#xff0c;我们将会认识到一个子类会比一个极其灵活的超类还要更智能。

我们可以(也应该)通过混合的位置和关键字实现生成这些&#xff0c;如下面的代码片段所示&#xff1a;

class Player3(Player):

def __init__(self, table, bet_strategy, game_strategy, **extras):

self.bet_strategy &#61; bet_strategy

self.game_strategy &#61; game_strategy

self.table&#61; table

self.__dict__.update(extras)

这比完全开放定义更明智。我们已经取得了所需的位置参数。我们留下任何非必需参数作为关键字。这个阐明了__init__()给出的任何额外的关键字参数的使用。

这种灵活的关键字初始化取决于我们是否有相对透明的类定义。这种开放的态度面对改变需要注意避免调试名称冲突&#xff0c;因为关键字参数名是开放式的。

1. 初始化类型验证

类型验证很少是一个合理的要求。在某种程度上&#xff0c;是没有对Python完全理解。名义目标是验证所有参数是否是一个合适的类型。试图这样做的原因主要是因为适当的定义往往是过于狭隘以至于没有什么真正的用途。

这不同于确认对象满足其他条件。数字范围检查&#xff0c;例如&#xff0c;防止无限循环的必要。

我们可以制造问题去试图做些什么&#xff0c;就像下面__init__()方法中那样&#xff1a;

class ValidPlayer:

def __init__(self, table, bet_strategy, game_strategy):

assert isinstance(table, Table)

assert isinstance(bet_strategy, BettingStrategy)

assert isinstance(game_strategy, GameStrategy)

self.bet_strategy &#61; bet_strategy

self.game_strategy &#61; game_strategy

self.table &#61; table

isinstance()方法检查、规避Python的标准鸭子类型。

我们写一个赌场游戏模拟是为了尝试不断变化的GameStrategy。这些很简单(仅仅四个方法)&#xff0c;几乎没有从超类的继承中得到任何帮助。我们可以独立的定义缺乏整体的超类。

这个示例中所示的初始化错误检查&#xff0c;将迫使我们通过错误检查的创建子类。没有可用的代码是继承自抽象超类。

最大的一个鸭子类型问题就围绕数值类型。不同的数值类型将工作在不同的上下文中。试图验证类型的争论可能会阻止一个完美合理的数值类型正常工作。当尝试验证时&#xff0c;我们有以下两个选择在Python中&#xff1a;

我们编写验证&#xff0c;这样一个相对狭窄的集合类型是允许的&#xff0c;总有一天代码会因为聪明的新类型被禁止而中断。

我们避开验证&#xff0c;这样一个相对广泛的集合类型是允许的&#xff0c;总有一天代码会因为不聪明地类型被使用而中断。

注意&#xff0c;两个本质上是相同的。代码可能有一天被中断。要么因为禁止使用即使它是聪明&#xff0c;要么因为不聪明的使用。

让它

一般来说&#xff0c;更好的Python风格就是简单地允许使用任何类型的数据。

我们将在第4章《一致设计的基本知识》回到这个问题。

这个问题是&#xff1a;为什么限制未来潜在的用例&#xff1f;

通常回答是&#xff0c;没有理由限制未来潜在的用例。

比起阻止一个聪明的&#xff0c;但可能是意料之外的用例&#xff0c;我们可以提供文档、测试和调试日志帮助其他程序员理解任何可以处理的限制类型。我们必须提供文档、日志和测试用例&#xff0c;这样额外的工作开销最小。

下面是一个示例文档字符串&#xff0c;它提供了对类的预期&#xff1a;

class Player:

def __init__(self, table, bet_strategy, game_strategy):

"""Creates a new player associated with a table,

and configured with proper betting and play strategies

:param table: an instance of :class:&#96;Table&#96;

:param bet_strategy: an instance of :class:&#96;BettingStrategy&#96;

:param game_strategy: an instance of :class:&#96;GameStrategy&#96;

"""

self.bet_strategy &#61; bet_strategy

self.game_strategy &#61; game_strategy

self.table &#61; table

程序员使用这个类已经被警告了限制类型是什么。其他类型的使用是被允许的。如果类型不符合预期&#xff0c;执行会中断。理想情况下&#xff0c;我们将使用unittest和doctest来发现bug。

2. 初始化、封装和私有

一般Python关于私有的政策可以总结如下&#xff1a;我们都是成年人了。

面向对象的设计有显式接口和实现之间的区别。这是封装的结果。类封装了数据结构、算法、一个外部接口或者一些有意义的事情。这个想法是从实现细节封装分离基于类的接口。

但是&#xff0c;没有编程语言反映了每一个设计细节。Python中&#xff0c;通常情况下&#xff0c;并没有考虑都用显式代码实现所有设计。

类的设计&#xff0c;一方面是没有完全在代码中有私有(实现)和公有(接口)方法或属性对象的区别。私有的概念主要来自(c&#43;&#43;或Java)语言&#xff0c;这已经很复杂了。这些语言设置包括如私有、保护、和公有以及“未指定”&#xff0c;这是一种半专用的。私有关键字的使用不当&#xff0c;通常使得子类定义产生不必要的困难。

Python私有的概念很简单&#xff0c;如下

本质上都是公有的。源代码是可用的。我们都是成年人。没有什么可以真正隐藏的。

一般来说&#xff0c;我们会把一些名字的方式公开。他们普遍实现细节&#xff0c;如有变更&#xff0c;恕不另行通知&#xff0c;但是没有正式的私有的概念。

在部分Python中&#xff0c;命名以_开头的一般是非公有的。help()函数通常忽略了这些方法。Sphinx等工具可以从文档隐藏这些名字。

Python的内部命名是以__开始(结束)的。这就是Python保持内部不与应用程序的命名起冲突。这些内部的集合名称完全是由语言内部参考定义的。此外&#xff0c;在我们的代码中尝试使用__试图创建“超级私人”属性或方法是没有任何好处的。一旦Python的发行版本开始使用我们选择内部使用的命名&#xff0c;会造成潜在的问题。同样&#xff0c;我们使用这些命名很可能与内部命名发生冲突。

Python的命名规则如下&#xff1a;

大多数命名是公有的。

以_开头的都是非公有的。使用它们来实现细节是真正可能发生变化的。

以__开头或结尾的命名是Python内部的。我们不能这样命名&#xff1b;我们使用语言参考定义的名称。

一般情况下&#xff0c;Python方法使用文档和好的命名来表达一个方法(或属性)的意图。通常&#xff0c;接口方法会有复杂的文档&#xff0c;可能包括doctest的示例&#xff0c;而实现方法将有更多的简写文档&#xff0c;很可能没有doctest示例。

新手Python程序员&#xff0c;有时奇怪私有没有得到更广泛的使用。而经验丰富的Python程序员&#xff0c;却惊讶于为了整理并不实用的私有和公有声明去消耗大脑的卡路里&#xff0c;因为从方法的命名和文档中就能知道变量名的意图。

总结

在本章中&#xff0c;我们回顾了__init__()方法的各种设计方案。在下一章&#xff0c;我们将看一看特别的以及一些高级的方法。



推荐阅读
  • 本文介绍了GregorianCalendar类的基本信息,包括它是Calendar的子类,提供了世界上大多数国家使用的标准日历系统。默认情况下,它对应格里高利日历创立时的日期,但可以通过调用setGregorianChange()方法来更改起始日期。同时,文中还提到了GregorianCalendar类为每个日历字段使用的默认值。 ... [详细]
  • 上图是InnoDB存储引擎的结构。1、缓冲池InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。因此可以看作是基于磁盘的数据库系统。在数据库系统中,由于CPU速度 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 3.223.28周学习总结中的贪心作业收获及困惑
    本文是对3.223.28周学习总结中的贪心作业进行总结,作者在解题过程中参考了他人的代码,但前提是要先理解题目并有解题思路。作者分享了自己在贪心作业中的收获,同时提到了一道让他困惑的题目,即input details部分引发的疑惑。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • 本文讨论了如何使用IF函数从基于有限输入列表的有限输出列表中获取输出,并提出了是否有更快/更有效的执行代码的方法。作者希望了解是否有办法缩短代码,并从自我开发的角度来看是否有更好的方法。提供的代码可以按原样工作,但作者想知道是否有更好的方法来执行这样的任务。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 第四章高阶函数(参数传递、高阶函数、lambda表达式)(python进阶)的讲解和应用
    本文主要讲解了第四章高阶函数(参数传递、高阶函数、lambda表达式)的相关知识,包括函数参数传递机制和赋值机制、引用传递的概念和应用、默认参数的定义和使用等内容。同时介绍了高阶函数和lambda表达式的概念,并给出了一些实例代码进行演示。对于想要进一步提升python编程能力的读者来说,本文将是一个不错的学习资料。 ... [详细]
  • 本文介绍了在处理不规则数据时如何使用Python自动提取文本中的时间日期,包括使用dateutil.parser模块统一日期字符串格式和使用datefinder模块提取日期。同时,还介绍了一段使用正则表达式的代码,可以支持中文日期和一些特殊的时间识别,例如'2012年12月12日'、'3小时前'、'在2012/12/13哈哈'等。 ... [详细]
  • 本文介绍了在iOS开发中使用UITextField实现字符限制的方法,包括利用代理方法和使用BNTextField-Limit库的实现策略。通过这些方法,开发者可以方便地限制UITextField的字符个数和输入规则。 ... [详细]
  • 十大经典排序算法动图演示+Python实现
    本文介绍了十大经典排序算法的原理、演示和Python实现。排序算法分为内部排序和外部排序,常见的内部排序算法有插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。文章还解释了时间复杂度和稳定性的概念,并提供了相关的名词解释。 ... [详细]
author-avatar
mobiledu2502927723
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有